Handle a condition when the result of getting configured networks returns null am: 3abc7f0561
am: 3b17189719
Change-Id: I6f2c96f8ef75e97e5df867a6122ee94b0060d0ad
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..2673295
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,7 @@
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+
+[Builtin Hooks]
+commit_msg_bug_field = true
+commit_msg_changeid_field = true
+commit_msg_test_field = true
diff --git a/libwifi_hal/Android.mk b/libwifi_hal/Android.mk
new file mode 100644
index 0000000..1179a09
--- /dev/null
+++ b/libwifi_hal/Android.mk
@@ -0,0 +1,140 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+wifi_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
+
+# Some devices use a different path (e.g. devices with broadcom WiFi parts).
+ifdef WIFI_DRIVER_FW_PATH_PARAM
+wifi_hal_cflags += -DWIFI_DRIVER_FW_PATH_PARAM=\"$(WIFI_DRIVER_FW_PATH_PARAM)\"
+else
+wifi_hal_cflags += -DWIFI_DRIVER_FW_PATH_PARAM=\"/sys/module/wlan/parameters/fwpath\"
+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_VENDOR_MODULE := true
+LOCAL_CFLAGS := $(wifi_hal_cflags)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := libbase
+LOCAL_HEADER_LIBRARIES := libcutils_headers
+LOCAL_SRC_FILES := wifi_hal_common.cpp
+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_VENDOR_MODULE := true
+LOCAL_CFLAGS := $(wifi_hal_cflags)
+LOCAL_SRC_FILES := wifi_hal_fallback.cpp
+LOCAL_HEADER_LIBRARIES := libhardware_legacy_headers
+include $(BUILD_STATIC_LIBRARY)
+
+# Pick a vendor provided HAL implementation library.
+# ============================================================
+LIB_WIFI_HAL := libwifi-hal-fallback
+VENDOR_LOCAL_SHARED_LIBRARIES :=
+ifeq ($(BOARD_WLAN_DEVICE), bcmdhd)
+ LIB_WIFI_HAL := libwifi-hal-bcm
+else ifeq ($(BOARD_WLAN_DEVICE), qcwcn)
+ LIB_WIFI_HAL := libwifi-hal-qcom
+ VENDOR_LOCAL_SHARED_LIBRARIES := libcld80211
+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_PROPRIETARY_MODULE := true
+LOCAL_CFLAGS := $(wifi_hal_cflags)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH)/include
+LOCAL_EXPORT_HEADER_LIBRARY_HEADERS := libhardware_legacy_headers
+LOCAL_HEADER_LIBRARIES := libhardware_legacy_headers
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libcutils \
+ liblog \
+ libnl \
+ libutils \
+ $(VENDOR_LOCAL_SHARED_LIBRARIES)
+LOCAL_SRC_FILES := \
+ driver_tool.cpp \
+ hal_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)
diff --git a/libwifi_hal/CleanSpec.mk b/libwifi_hal/CleanSpec.mk
new file mode 100644
index 0000000..d7a50f6
--- /dev/null
+++ b/libwifi_hal/CleanSpec.mk
@@ -0,0 +1,52 @@
+# -*- mode: makefile -*-
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(OUT_DIR)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+#
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/obj/SHARED_LIBRARIES/libdvm*)
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libwifi-hal*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libwifi-hal*)
diff --git a/libwifi_hal/driver_tool.cpp b/libwifi_hal/driver_tool.cpp
new file mode 100644
index 0000000..3089ee0
--- /dev/null
+++ b/libwifi_hal/driver_tool.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 <android-base/logging.h>
+#include <private/android_filesystem_config.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::TakeOwnershipOfFirmwareReload() {
+ if (!wifi_get_fw_path(kFirmwareModeSta) &&
+ !wifi_get_fw_path(kFirmwareModeAp) &&
+ !wifi_get_fw_path(kFirmwareModeP2p)) {
+ return true; // HAL doesn't think we need to load firmware for any mode.
+ }
+
+ if (chown(WIFI_DRIVER_FW_PATH_PARAM, AID_WIFI, AID_WIFI) != 0) {
+ PLOG(ERROR) << "Error changing ownership of '" << WIFI_DRIVER_FW_PATH_PARAM
+ << "' to wifi:wifi";
+ return false;
+ }
+
+ return true;
+}
+
+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::IsFirmwareModeChangeNeeded(int mode) {
+ return (wifi_get_fw_path(mode) != nullptr);
+}
+
+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/hal_tool.cpp b/libwifi_hal/hal_tool.cpp
new file mode 100644
index 0000000..76b57aa
--- /dev/null
+++ b/libwifi_hal/hal_tool.cpp
@@ -0,0 +1,578 @@
+/*
+ * 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/hal_tool.h"
+
+#include <android-base/logging.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_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_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)) {
+ LOG(ERROR) << "Can not initialize the basic function pointer table";
+ return false;
+ }
+
+ if (init_wifi_vendor_hal_func_table(hal_fn) != WIFI_SUCCESS) {
+ LOG(ERROR) << "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_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..a27641b
--- /dev/null
+++ b/libwifi_hal/include/wifi_hal/driver_tool.h
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+ // Change the owner of the firmware reload path to wifi:wifi if
+ // firmware reload is supported.
+ static bool TakeOwnershipOfFirmwareReload();
+
+ 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();
+
+ // Check if we need to invoke |ChangeFirmwareMode| to configure
+ // the firmware for the provided mode.
+ // |mode| is one of the kFirmwareMode* constants defined above.
+ // Returns true if needed, and false otherwise.
+ virtual bool IsFirmwareModeChangeNeeded(int mode);
+
+ // 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/include/wifi_hal/hal_tool.h b/libwifi_hal/include/wifi_hal/hal_tool.h
new file mode 100644
index 0000000..b25893e
--- /dev/null
+++ b/libwifi_hal/include/wifi_hal/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_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..04e5925
--- /dev/null
+++ b/libwifi_hal/wifi_hal_common.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <cutils/misc.h>
+#include <cutils/properties.h>
+
+extern "C" int init_module(void *, unsigned long, const char *);
+extern "C" 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_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)
+ PLOG(DEBUG) << "Unable to unload driver module '" << modname << "'";
+ 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) {
+ PLOG(ERROR) << "Failed to open driver state control param";
+ return -1;
+ }
+ len = strlen(state) + 1;
+ if (TEMP_FAILURE_RETRY(write(fd, state, len)) != len) {
+ PLOG(ERROR) << "Failed to write driver state control param";
+ 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) {
+ PLOG(WARNING) << "Could not open " << MODULE_FILE;
+ 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;
+#endif
+
+#ifdef 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() {
+ if (!is_wifi_driver_loaded()) {
+ return 0;
+ }
+ 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) {
+ PLOG(ERROR) << "Failed to open wlan fw path param";
+ return -1;
+ }
+ len = strlen(fwpath) + 1;
+ if (TEMP_FAILURE_RETRY(write(fd, fwpath, len)) != len) {
+ PLOG(ERROR) << "Failed to write wlan fw path param";
+ 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..fd0fff4
--- /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..5541867
--- /dev/null
+++ b/libwifi_system/Android.mk
@@ -0,0 +1,76 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+wifi_system_cflags := \
+ -Wall \
+ -Werror \
+ -Wextra \
+ -Winit-self \
+ -Wno-unused-function \
+ -Wno-unused-parameter \
+ -Wshadow \
+ -Wunused-variable \
+ -Wwrite-strings
+
+# Device independent wifi system logic.
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwifi-system
+LOCAL_CFLAGS := $(wifi_system_cflags)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := libbase
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libcrypto \
+ libcutils
+
+LOCAL_SRC_FILES := \
+ hostapd_manager.cpp \
+ interface_tool.cpp \
+ supplicant_manager.cpp
+include $(BUILD_SHARED_LIBRARY)
+
+# Test utilities (e.g. mock classes) for libwifi-system
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwifi-system-test
+LOCAL_CFLAGS := $(wifi_system_cflags)
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/testlib/include
+LOCAL_STATIC_LIBRARIES := libgmock
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/testlib/include
+include $(BUILD_STATIC_LIBRARY)
+
+
+# Unit tests for libwifi-system
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwifi-system_tests
+LOCAL_CPPFLAGS := $(wificond_cpp_flags)
+LOCAL_SRC_FILES := \
+ tests/main.cpp \
+ tests/hostapd_manager_unittest.cpp
+LOCAL_STATIC_LIBRARIES := \
+ libgmock \
+ libgtest
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libwifi-system
+include $(BUILD_NATIVE_TEST)
diff --git a/libwifi_system/hostapd_manager.cpp b/libwifi_system/hostapd_manager.cpp
new file mode 100644
index 0000000..68184e9
--- /dev/null
+++ b/libwifi_system/hostapd_manager.cpp
@@ -0,0 +1,198 @@
+/*
+ * 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/parseint.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/supplicant_manager.h"
+
+using android::base::ParseInt;
+using android::base::ReadFileToString;
+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 (!SupplicantManager::EnsureEntropyFileExists()) {
+ 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";
+ return true;
+}
+
+bool HostapdManager::StopHostapd() {
+ 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";
+ return true;
+}
+
+bool HostapdManager::WriteHostapdConfig(const string& config) {
+ if (!WriteStringToFile(config, kHostapdConfigFilePath,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
+ AID_WIFI, AID_WIFI)) {
+ int error = errno;
+ LOG(ERROR) << "Cannot write hostapd config to \""
+ << kHostapdConfigFilePath << "\": " << strerror(error);
+ struct stat st;
+ int result = stat(kHostapdConfigFilePath, &st);
+ if (result == 0) {
+ LOG(ERROR) << "hostapd config file uid: "<< st.st_uid << ", gid: " << st.st_gid
+ << ", mode: " << st.st_mode;
+ } else {
+ LOG(ERROR) << "Error calling stat() on hostapd config file: " << strerror(errno);
+ }
+ 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/ctrl\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/hostapd_manager.h b/libwifi_system/include/wifi_system/hostapd_manager.h
new file mode 100644
index 0000000..4852670
--- /dev/null
+++ b/libwifi_system/include/wifi_system/hostapd_manager.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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();
+
+ // 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:
+ 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..aabdd9a
--- /dev/null
+++ b/libwifi_system/include/wifi_system/interface_tool.h
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+ // Get the interface state of |if_name|.
+ // Returns true iff the interface is up.
+ virtual bool GetUpState(const char* if_name);
+
+ // 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/supplicant_manager.h b/libwifi_system/include/wifi_system/supplicant_manager.h
new file mode 100644
index 0000000..e6a5789
--- /dev/null
+++ b/libwifi_system/include/wifi_system/supplicant_manager.h
@@ -0,0 +1,51 @@
+/*
+ * 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_SUPPLICANT_MANAGER_H
+#define ANDROID_WIFI_SYSTEM_SUPPLICANT_MANAGER_H
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace wifi_system {
+
+class SupplicantManager {
+ public:
+ SupplicantManager() = default;
+ virtual ~SupplicantManager() = default;
+
+ // Request that supplicant be started.
+ // Returns true on success.
+ virtual bool StartSupplicant();
+
+ // Request that a running instance of supplicant be stopped.
+ // Returns true on success.
+ virtual bool StopSupplicant();
+
+ // Returns true iff supplicant is still running.
+ virtual bool IsSupplicantRunning();
+
+ // Returns true iff supplicant entropy file exists.
+ static bool EnsureEntropyFileExists();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SupplicantManager);
+}; // class SupplicantManager
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_SUPPLICANT_MANAGER_H
diff --git a/libwifi_system/interface_tool.cpp b/libwifi_system/interface_tool.cpp
new file mode 100644
index 0000000..f0d40ef
--- /dev/null
+++ b/libwifi_system/interface_tool.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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/logging.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+const char kWlan0InterfaceName[] = "wlan0";
+
+bool GetIfState(const char* if_name, int sock, struct ifreq* ifr) {
+ memset(ifr, 0, sizeof(*ifr));
+ if (strlcpy(ifr->ifr_name, if_name, sizeof(ifr->ifr_name)) >=
+ sizeof(ifr->ifr_name)) {
+ LOG(ERROR) << "Interface name is too long: " << if_name;
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(ioctl(sock, SIOCGIFFLAGS, ifr)) != 0) {
+ LOG(ERROR) << "Could not read interface state for " << if_name
+ << " (" << strerror(errno) << ")";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool InterfaceTool::GetUpState(const char* if_name) {
+ base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ if (sock.get() < 0) {
+ LOG(ERROR) << "Failed to open socket to set up/down state ("
+ << strerror(errno) << ")";
+ return false;
+ }
+
+ struct ifreq ifr;
+ if (!GetIfState(if_name, sock.get(), &ifr)) {
+ return false; // logging done internally
+ }
+
+ return ifr.ifr_flags & IFF_UP;
+}
+
+bool InterfaceTool::SetUpState(const char* if_name, bool request_up) {
+ base::unique_fd sock(socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ if (sock.get() < 0) {
+ LOG(ERROR) << "Failed to open socket to set up/down state ("
+ << strerror(errno) << ")";
+ return false;
+ }
+
+ struct ifreq ifr;
+ if (!GetIfState(if_name, sock.get(), &ifr)) {
+ return false; // logging done internally
+ }
+
+ 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) {
+ LOG(ERROR) << "Could not set interface flags for " << if_name
+ << " (" << strerror(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/supplicant_manager.cpp b/libwifi_system/supplicant_manager.cpp
new file mode 100644
index 0000000..28010ec
--- /dev/null
+++ b/libwifi_system/supplicant_manager.cpp
@@ -0,0 +1,267 @@
+/*
+ * 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/supplicant_manager.h"
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+// This ugliness is necessary to access internal implementation details
+// of the property subsystem.
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+namespace android {
+namespace wifi_system {
+namespace {
+
+const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant";
+const char kSupplicantConfigTemplatePath[] =
+ "/etc/wifi/wpa_supplicant.conf";
+const char kSupplicantConfigFile[] = "/data/misc/wifi/wpa_supplicant.conf";
+const char kP2pConfigFile[] = "/data/misc/wifi/p2p_supplicant.conf";
+const char kSupplicantServiceName[] = "wpa_supplicant";
+constexpr mode_t kConfigFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+const char kWiFiEntropyFile[] = "/data/misc/wifi/entropy.bin";
+
+const unsigned char kDummyKey[21] = {0x02, 0x11, 0xbe, 0x33, 0x43, 0x35, 0x68,
+ 0x47, 0x84, 0x99, 0xa9, 0x2b, 0x1c, 0xd3,
+ 0xee, 0xff, 0xf1, 0xe2, 0xf3, 0xf4, 0xf5};
+
+int ensure_config_file_exists(const char* config_file) {
+ char buf[2048];
+ int srcfd, destfd;
+ int nread;
+ int ret;
+ std::string templatePath;
+
+ ret = access(config_file, R_OK | W_OK);
+ if ((ret == 0) || (errno == EACCES)) {
+ if ((ret != 0) && (chmod(config_file, kConfigFileMode) != 0)) {
+ LOG(ERROR) << "Cannot set RW to \"" << config_file << "\": "
+ << strerror(errno);
+ return false;
+ }
+ return true;
+ } else if (errno != ENOENT) {
+ LOG(ERROR) << "Cannot access \"" << config_file << "\": "
+ << strerror(errno);
+ return false;
+ }
+
+ std::string configPathSystem =
+ std::string("/system") + std::string(kSupplicantConfigTemplatePath);
+ std::string configPathVendor =
+ std::string("/vendor") + std::string(kSupplicantConfigTemplatePath);
+ srcfd = TEMP_FAILURE_RETRY(open(configPathSystem.c_str(), O_RDONLY));
+ templatePath = configPathSystem;
+ if (srcfd < 0) {
+ int errnoSystem = errno;
+ srcfd = TEMP_FAILURE_RETRY(open(configPathVendor.c_str(), O_RDONLY));
+ templatePath = configPathVendor;
+ if (srcfd < 0) {
+ int errnoVendor = errno;
+ LOG(ERROR) << "Cannot open \"" << configPathSystem << "\": "
+ << strerror(errnoSystem);
+ LOG(ERROR) << "Cannot open \"" << configPathVendor << "\": "
+ << strerror(errnoVendor);
+ return false;
+ }
+ }
+
+ destfd = TEMP_FAILURE_RETRY(open(config_file,
+ O_CREAT | O_RDWR,
+ kConfigFileMode));
+ if (destfd < 0) {
+ close(srcfd);
+ LOG(ERROR) << "Cannot create \"" << config_file << "\": "
+ << strerror(errno);
+ return false;
+ }
+
+ while ((nread = TEMP_FAILURE_RETRY(read(srcfd, buf, sizeof(buf)))) != 0) {
+ if (nread < 0) {
+ LOG(ERROR) << "Error reading \"" << templatePath
+ << "\": " << strerror(errno);
+ close(srcfd);
+ close(destfd);
+ unlink(config_file);
+ return false;
+ }
+ 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, kConfigFileMode) < 0) {
+ LOG(ERROR) << "Error changing permissions of " << config_file
+ << " to 0660: " << strerror(errno);
+ unlink(config_file);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+bool SupplicantManager::StartSupplicant() {
+ char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+ int count = 200; /* wait at most 20 seconds for completion */
+ const prop_info* pi;
+ unsigned serial = 0;
+
+ /* Check whether already running */
+ if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
+ strcmp(supp_status, "running") == 0) {
+ return true;
+ }
+
+ /* Before starting the daemon, make sure its config file exists */
+ if (ensure_config_file_exists(kSupplicantConfigFile) < 0) {
+ LOG(ERROR) << "Wi-Fi will not be enabled";
+ return false;
+ }
+
+ /*
+ * Some devices have another configuration file for the p2p interface.
+ * However, not all devices have this, and we'll let it slide if it
+ * is missing. For devices that do expect this file to exist,
+ * supplicant will refuse to start and emit a good error message.
+ * No need to check for it here.
+ */
+ (void)ensure_config_file_exists(kP2pConfigFile);
+
+ if (!EnsureEntropyFileExists()) {
+ LOG(ERROR) << "Wi-Fi entropy file was not created";
+ }
+
+ /*
+ * 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(kSupplicantInitProperty);
+ if (pi != NULL) {
+ serial = __system_property_serial(pi);
+ }
+
+ property_set("ctl.start", kSupplicantServiceName);
+ sched_yield();
+
+ while (count-- > 0) {
+ if (pi == NULL) {
+ pi = __system_property_find(kSupplicantInitProperty);
+ }
+ 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 true;
+ } else if (strcmp(supp_status, "stopped") == 0) {
+ return false;
+ }
+ }
+ }
+ usleep(100000);
+ }
+ return false;
+}
+
+bool SupplicantManager::StopSupplicant() {
+ char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+ int count = 50; /* wait at most 5 seconds for completion */
+
+ /* Check whether supplicant already stopped */
+ if (property_get(kSupplicantInitProperty, supp_status, NULL) &&
+ strcmp(supp_status, "stopped") == 0) {
+ return true;
+ }
+
+ property_set("ctl.stop", kSupplicantServiceName);
+ sched_yield();
+
+ while (count-- > 0) {
+ if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
+ if (strcmp(supp_status, "stopped") == 0) return true;
+ }
+ usleep(100000);
+ }
+ LOG(ERROR) << "Failed to stop supplicant";
+ return false;
+}
+
+bool SupplicantManager::IsSupplicantRunning() {
+ char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+ if (property_get(kSupplicantInitProperty, supp_status, NULL)) {
+ return strcmp(supp_status, "running") == 0;
+ }
+ return false; // Failed to read service status from init.
+}
+
+bool SupplicantManager::EnsureEntropyFileExists() {
+ 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)) {
+ PLOG(ERROR) << "Cannot set RW to " << kWiFiEntropyFile;
+ return false;
+ }
+ return true;
+ }
+ destfd = TEMP_FAILURE_RETRY(open(kWiFiEntropyFile, O_CREAT | O_RDWR, 0660));
+ if (destfd < 0) {
+ PLOG(ERROR) << "Cannot create " << kWiFiEntropyFile;
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(write(destfd, kDummyKey, sizeof(kDummyKey))) !=
+ sizeof(kDummyKey)) {
+ PLOG(ERROR) << "Error writing " << kWiFiEntropyFile;
+ close(destfd);
+ return false;
+ }
+ close(destfd);
+
+ /* chmod is needed because open() didn't set permisions properly */
+ if (chmod(kWiFiEntropyFile, 0660) < 0) {
+ PLOG(ERROR) << "Error changing permissions of " << kWiFiEntropyFile
+ << " to 0600 ";
+ unlink(kWiFiEntropyFile);
+ return false;
+ }
+
+ return true;
+}
+
+} // 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_hostapd_manager.h b/libwifi_system/testlib/include/wifi_system_test/mock_hostapd_manager.h
new file mode 100644
index 0000000..6dbeafe
--- /dev/null
+++ b/libwifi_system/testlib/include/wifi_system_test/mock_hostapd_manager.h
@@ -0,0 +1,46 @@
+/*
+ * 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_HOSTAPD_MANAGER_H
+#define ANDROID_WIFI_SYSTEM_TEST_MOCK_HOSTAPD_MANAGER_H
+
+#include <wifi_system/hostapd_manager.h>
+
+namespace android {
+namespace wifi_system {
+
+class MockHostapdManager : public HostapdManager {
+ public:
+ ~MockHostapdManager() override = default;
+
+ MOCK_METHOD0(StartHostapd, bool());
+ MOCK_METHOD0(IsHostapdRunning, bool());
+ MOCK_METHOD0(StopHostapd, bool());
+ MOCK_METHOD6(CreateHostapdConfig, std::string(
+ const std::string& interface_name,
+ const std::vector<uint8_t> ssid,
+ bool is_hidden,
+ int channel,
+ EncryptionType encryption,
+ const std::vector<uint8_t> passphrase));
+ MOCK_METHOD1(WriteHostapdConfig, bool(const std::string& config_file));
+
+}; // class MockHostapdManager
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_TEST_MOCK_HOSTAPD_MANAGER_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..b9926c9
--- /dev/null
+++ b/libwifi_system/testlib/include/wifi_system_test/mock_interface_tool.h
@@ -0,0 +1,38 @@
+/*
+ * 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_METHOD1(GetUpState, bool(const char* if_name));
+ 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/testlib/include/wifi_system_test/mock_supplicant_manager.h b/libwifi_system/testlib/include/wifi_system_test/mock_supplicant_manager.h
new file mode 100644
index 0000000..01d604f
--- /dev/null
+++ b/libwifi_system/testlib/include/wifi_system_test/mock_supplicant_manager.h
@@ -0,0 +1,38 @@
+/*
+ * 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_SUPPLICANT_MANAGER_H
+#define ANDROID_WIFI_SYSTEM_TEST_MOCK_SUPPLICANT_MANAGER_H
+
+#include <wifi_system/supplicant_manager.h>
+
+namespace android {
+namespace wifi_system {
+
+class MockSupplicantManager : public SupplicantManager {
+ public:
+ ~MockSupplicantManager() override = default;
+
+ MOCK_METHOD0(StartSupplicant, bool());
+ MOCK_METHOD0(StopSupplicant, bool());
+ MOCK_METHOD0(IsSupplicantRunning, bool());
+
+}; // class MockSupplicantManager
+
+} // namespace wifi_system
+} // namespace android
+
+#endif // ANDROID_WIFI_SYSTEM_TEST_MOCK_SUPPLICANT_MANAGER_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..048d473
--- /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/ctrl\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/service/Android.mk b/service/Android.mk
index 8201acf..b6288be 100644
--- a/service/Android.mk
+++ b/service/Android.mk
@@ -16,129 +16,58 @@
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 += \
+ liblog \
libnativehelper \
libcutils \
libutils \
- libhardware \
- libhardware_legacy \
- libnl \
libdl
-LOCAL_STATIC_LIBRARIES += libwifi-hal-stub
-LOCAL_STATIC_LIBRARIES += $(LIB_WIFI_HAL)
-
LOCAL_SRC_FILES := \
jni/com_android_server_wifi_WifiNative.cpp \
jni/jni_helper.cpp
-ifdef INCLUDE_NAN_FEATURE
-LOCAL_SRC_FILES += \
- jni/com_android_server_wifi_nan_WifiNanNative.cpp
-endif
-
LOCAL_MODULE := libwifi-service
-# b/22172328
-LOCAL_CLANG := false
include $(BUILD_SHARED_LIBRARY)
# 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-logtags-files-under, java) \
- $(call all-proto-files-under, proto)
+ $(call all-Iaidl-files-under, $(wificond_aidl_rel_path)) \
+ $(call all-logtags-files-under, java)
-ifndef INCLUDE_NAN_FEATURE
-LOCAL_SRC_FILES := $(filter-out $(call all-java-files-under, \
- java/com/android/server/wifi/nan),$(LOCAL_SRC_FILES))
-endif
-
-LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt services
+LOCAL_JAVA_LIBRARIES := \
+ android.hidl.manager-V1.0-java \
+ bouncycastle \
+ conscrypt \
+ jsr305 \
+ services
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android.hardware.wifi-V1.0-java \
+ android.hardware.wifi.supplicant-V1.0-java
LOCAL_REQUIRED_MODULES := services
LOCAL_MODULE_TAGS :=
LOCAL_MODULE := wifi-service
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+LOCAL_INIT_RC := wifi-events.rc
ifeq ($(EMMA_INSTRUMENT_FRAMEWORK),true)
LOCAL_EMMA_INSTRUMENT := true
@@ -148,4 +77,4 @@
include $(BUILD_JAVA_LIBRARY)
-endif
+endif # !TARGET_BUILD_PDK
diff --git a/service/java/com/android/server/wifi/ActiveModeManager.java b/service/java/com/android/server/wifi/ActiveModeManager.java
new file mode 100644
index 0000000..00ae7b3
--- /dev/null
+++ b/service/java/com/android/server/wifi/ActiveModeManager.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * Base class for available WiFi operating modes.
+ *
+ * Currently supported modes include Client, ScanOnly and SoftAp.
+ */
+public interface ActiveModeManager {
+ String TAG = "ActiveModeManager";
+
+ /**
+ * Method used to start the Manager for a given Wifi operational mode.
+ */
+ void start();
+
+ /**
+ * Method used to stop the Manager for a give Wifi operational mode.
+ */
+ void stop();
+}
diff --git a/service/java/com/android/server/wifi/BaseWifiDiagnostics.java b/service/java/com/android/server/wifi/BaseWifiDiagnostics.java
new file mode 100644
index 0000000..54fff68
--- /dev/null
+++ b/service/java/com/android/server/wifi/BaseWifiDiagnostics.java
@@ -0,0 +1,60 @@
+
+package com.android.server.wifi;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ *
+ */
+public class BaseWifiDiagnostics {
+ public static final byte CONNECTION_EVENT_STARTED = 0;
+ public static final byte CONNECTION_EVENT_SUCCEEDED = 1;
+ public static final byte CONNECTION_EVENT_FAILED = 2;
+
+ protected final WifiNative mWifiNative;
+
+ protected String mFirmwareVersion;
+ protected String mDriverVersion;
+ protected int mSupportedFeatureSet;
+
+ public BaseWifiDiagnostics(WifiNative wifiNative) {
+ mWifiNative = wifiNative;
+ }
+
+ public synchronized void startLogging(boolean verboseEnabled) {
+ mFirmwareVersion = mWifiNative.getFirmwareVersion();
+ mDriverVersion = mWifiNative.getDriverVersion();
+ mSupportedFeatureSet = mWifiNative.getSupportedLoggerFeatureSet();
+ }
+
+ public synchronized void startPacketLog() { }
+
+ public synchronized void stopPacketLog() { }
+
+ public synchronized void stopLogging() { }
+
+ /**
+ * Inform the diagnostics module of a connection event.
+ * @param connectionId A strictly increasing, non-negative, connection identifier
+ * @param event The type of connection event (see CONNECTION_EVENT_* constants)
+ */
+ synchronized void reportConnectionEvent(long connectionId, byte event) {}
+
+ public synchronized void captureBugReportData(int reason) { }
+
+ public synchronized void captureAlertData(int errorCode, byte[] alertData) { }
+
+ public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ dump(pw);
+ pw.println("*** firmware logging disabled, no debug data ****");
+ pw.println("set config_wifi_enable_wifi_firmware_debugging to enable");
+ }
+
+ protected synchronized void dump(PrintWriter pw) {
+ pw.println("Chipset information :-----------------------------------------------");
+ pw.println("FW Version is: " + mFirmwareVersion);
+ pw.println("Driver Version is: " + mDriverVersion);
+ pw.println("Supported Feature set: " + mSupportedFeatureSet);
+ }
+}
\ No newline at end of file
diff --git a/service/java/com/android/server/wifi/BaseWifiLogger.java b/service/java/com/android/server/wifi/BaseWifiLogger.java
deleted file mode 100644
index 43139d2..0000000
--- a/service/java/com/android/server/wifi/BaseWifiLogger.java
+++ /dev/null
@@ -1,49 +0,0 @@
-
-package com.android.server.wifi;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- *
- */
-public class BaseWifiLogger {
-
- protected String mFirmwareVersion;
- protected String mDriverVersion;
- protected int mSupportedFeatureSet;
-
- public BaseWifiLogger() { }
-
- public synchronized void startLogging(boolean verboseEnabled) {
- WifiNative wifiNative = WifiNative.getWlanNativeInterface();
- mFirmwareVersion = wifiNative.getFirmwareVersion();
- mDriverVersion = wifiNative.getDriverVersion();
- mSupportedFeatureSet = wifiNative.getSupportedLoggerFeatureSet();
- }
-
- public synchronized void startPacketLog() { }
-
- public synchronized void stopPacketLog() { }
-
- public synchronized void stopLogging() { }
-
- synchronized void reportConnectionFailure() {}
-
- public synchronized void captureBugReportData(int reason) { }
-
- public synchronized void captureAlertData(int errorCode, byte[] alertData) { }
-
- public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- dump(pw);
- pw.println("*** firmware logging disabled, no debug data ****");
- pw.println("set config_wifi_enable_wifi_firmware_debugging to enable");
- }
-
- protected synchronized void dump(PrintWriter pw) {
- pw.println("Chipset information :-----------------------------------------------");
- pw.println("FW Version is: " + mFirmwareVersion);
- pw.println("Driver Version is: " + mDriverVersion);
- pw.println("Supported Feature set: " + mSupportedFeatureSet);
- }
-}
\ No newline at end of file
diff --git a/service/java/com/android/server/wifi/ByteBufferReader.java b/service/java/com/android/server/wifi/ByteBufferReader.java
new file mode 100644
index 0000000..91598af
--- /dev/null
+++ b/service/java/com/android/server/wifi/ByteBufferReader.java
@@ -0,0 +1,98 @@
+/*
+ * 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.Wifi
+ */
+
+package com.android.server.wifi;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+
+/**
+ * Utility class for reading generic data (e.g. various length integer, string) from ByteBuffer.
+ */
+public class ByteBufferReader {
+ @VisibleForTesting
+ public static final int MINIMUM_INTEGER_SIZE = Byte.BYTES;
+
+ @VisibleForTesting
+ public static final int MAXIMUM_INTEGER_SIZE = Long.BYTES;
+
+ /**
+ * Read an integer value from a buffer.
+ *
+ * @param payload The buffer to read from
+ * @param byteOrder Byte order of the buffer
+ * @param size The number of bytes to read from the buffer
+ * @return The integer value
+ * @throws BufferUnderflowException
+ * @throws IllegalArgumentException
+ */
+ public static long readInteger(ByteBuffer payload, ByteOrder byteOrder, int size) {
+ if (size < MINIMUM_INTEGER_SIZE || size > MAXIMUM_INTEGER_SIZE) {
+ throw new IllegalArgumentException("Invalid size " + size);
+ }
+
+ // Read the necessary bytes.
+ byte[] octets = new byte[size];
+ payload.get(octets);
+
+ // Format the value based on byte order.
+ long value = 0;
+ if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
+ for (int n = octets.length - 1; n >= 0; n--) {
+ value = (value << Byte.SIZE) | (octets[n] & 0xFF);
+ }
+ } else {
+ for (byte octet : octets) {
+ value = (value << Byte.SIZE) | (octet & 0xFF);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Read a string from a buffer. An empty String will be returned for a String with 0 length.
+ *
+ * @param payload The buffer to read from
+ * @param length Number of bytes to read from the buffer
+ * @param charset The character set of the string
+ * @return {@link String}
+ * @throws BufferUnderflowException
+ * @throws NegativeArraySizeException
+ */
+ public static String readString(ByteBuffer payload, int length, Charset charset) {
+ byte[] octets = new byte[length];
+ payload.get(octets);
+ return new String(octets, charset);
+ }
+
+ /**
+ * Read a string from a buffer where the string value is preceded by the length of the string
+ * (1 byte) in the buffer.
+ *
+ * @param payload The buffer to read from
+ * @param charset The character set of the string
+ * @return {@link String}
+ * @throws BufferUnderflowException
+ */
+ public static String readStringWithByteLength(ByteBuffer payload, Charset charset) {
+ int length = payload.get() & 0xFF;
+ return readString(payload, length, charset);
+ }
+}
diff --git a/service/java/com/android/server/wifi/ClientModeManager.java b/service/java/com/android/server/wifi/ClientModeManager.java
new file mode 100644
index 0000000..7ab33dd
--- /dev/null
+++ b/service/java/com/android/server/wifi/ClientModeManager.java
@@ -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.
+ */
+
+package com.android.server.wifi;
+
+/**
+ * Manager WiFi in Client Mode where we connect to configured networks.
+ */
+public class ClientModeManager implements ActiveModeManager {
+
+ private static final String TAG = "ClientModeManager";
+
+ ClientModeManager() {
+ }
+
+ /**
+ * Start client mode.
+ */
+ public void start() {
+
+ }
+
+ /**
+ * Disconnect from any currently connected networks and stop client mode.
+ */
+ public void stop() {
+
+ }
+}
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/ConfigurationMap.java b/service/java/com/android/server/wifi/ConfigurationMap.java
index a94ff0d..fc85f64 100644
--- a/service/java/com/android/server/wifi/ConfigurationMap.java
+++ b/service/java/com/android/server/wifi/ConfigurationMap.java
@@ -16,15 +16,9 @@
public class ConfigurationMap {
private final Map<Integer, WifiConfiguration> mPerID = new HashMap<>();
- private final Map<Integer, WifiConfiguration> mPerConfigKey = new HashMap<>();
private final Map<Integer, WifiConfiguration> mPerIDForCurrentUser = new HashMap<>();
private final Map<String, WifiConfiguration> mPerFQDNForCurrentUser = new HashMap<>();
- /**
- * List of all hidden networks in the current user's configuration.
- * Use this list as a param for directed scanning .
- */
- private final Set<Integer> mHiddenNetworkIdsForCurrentUser = new HashSet<>();
private final UserManager mUserManager;
@@ -37,16 +31,12 @@
// RW methods:
public WifiConfiguration put(WifiConfiguration config) {
final WifiConfiguration current = mPerID.put(config.networkId, config);
- mPerConfigKey.put(config.configKey().hashCode(), config); // This is ridiculous...
if (WifiConfigurationUtil.isVisibleToAnyProfile(config,
mUserManager.getProfiles(mCurrentUserId))) {
mPerIDForCurrentUser.put(config.networkId, config);
if (config.FQDN != null && config.FQDN.length() > 0) {
mPerFQDNForCurrentUser.put(config.FQDN, config);
}
- if (config.hiddenSSID) {
- mHiddenNetworkIdsForCurrentUser.add(config.networkId);
- }
}
return current;
}
@@ -56,7 +46,6 @@
if (config == null) {
return null;
}
- mPerConfigKey.remove(config.configKey().hashCode());
mPerIDForCurrentUser.remove(netID);
Iterator<Map.Entry<String, WifiConfiguration>> entries =
@@ -67,52 +56,22 @@
break;
}
}
- mHiddenNetworkIdsForCurrentUser.remove(netID);
return config;
}
public void clear() {
mPerID.clear();
- mPerConfigKey.clear();
mPerIDForCurrentUser.clear();
mPerFQDNForCurrentUser.clear();
- mHiddenNetworkIdsForCurrentUser.clear();
}
/**
- * Handles the switch to a different foreground user:
- * - Hides private network configurations belonging to the previous foreground user
- * - Reveals private network configurations belonging to the new foreground user
+ * Sets the new foreground user ID.
*
* @param userId the id of the new foreground user
- * @return a list of {@link WifiConfiguration}s that became hidden because of the user switch
*/
- public List<WifiConfiguration> handleUserSwitch(int userId) {
- mPerIDForCurrentUser.clear();
- mPerFQDNForCurrentUser.clear();
- mHiddenNetworkIdsForCurrentUser.clear();
-
- final List<UserInfo> previousUserProfiles = mUserManager.getProfiles(mCurrentUserId);
+ public void setNewUser(int userId) {
mCurrentUserId = userId;
- final List<UserInfo> currentUserProfiles = mUserManager.getProfiles(mCurrentUserId);
-
- final List<WifiConfiguration> hiddenConfigurations = new ArrayList<>();
- for (Map.Entry<Integer, WifiConfiguration> entry : mPerID.entrySet()) {
- final WifiConfiguration config = entry.getValue();
- if (WifiConfigurationUtil.isVisibleToAnyProfile(config, currentUserProfiles)) {
- mPerIDForCurrentUser.put(entry.getKey(), config);
- if (config.FQDN != null && config.FQDN.length() > 0) {
- mPerFQDNForCurrentUser.put(config.FQDN, config);
- }
- if (config.hiddenSSID) {
- mHiddenNetworkIdsForCurrentUser.add(config.networkId);
- }
- } else if (WifiConfigurationUtil.isVisibleToAnyProfile(config, previousUserProfiles)) {
- hiddenConfigurations.add(config);
- }
- }
-
- return hiddenConfigurations;
}
// RO methods:
@@ -148,10 +107,6 @@
return null;
}
- public WifiConfiguration getByConfigKeyIDForAllUsers(int id) {
- return mPerConfigKey.get(id);
- }
-
public Collection<WifiConfiguration> getEnabledNetworksForCurrentUser() {
List<WifiConfiguration> list = new ArrayList<>();
for (WifiConfiguration config : mPerIDForCurrentUser.values()) {
@@ -178,8 +133,4 @@
public Collection<WifiConfiguration> valuesForCurrentUser() {
return mPerIDForCurrentUser.values();
}
-
- public Set<Integer> getHiddenNetworkIdsForCurrentUser() {
- return mHiddenNetworkIdsForCurrentUser;
- }
}
diff --git a/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java
new file mode 100644
index 0000000..201a132
--- /dev/null
+++ b/service/java/com/android/server/wifi/DeletedEphemeralSsidsStoreData.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import com.android.server.wifi.util.XmlUtil;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class performs serialization and parsing of XML data block that contain the list of
+ * deleted ephemeral SSIDs (XML block data inside <DeletedEphemeralSSIDList> tag).
+ */
+public class DeletedEphemeralSsidsStoreData implements WifiConfigStore.StoreData {
+ private static final String XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST =
+ "DeletedEphemeralSSIDList";
+ private static final String XML_TAG_SSID_LIST = "SSIDList";
+
+ private Set<String> mSsidList;
+
+ DeletedEphemeralSsidsStoreData() {}
+
+ @Override
+ public void serializeData(XmlSerializer out, boolean shared)
+ throws XmlPullParserException, IOException {
+ if (shared) {
+ throw new XmlPullParserException("Share data not supported");
+ }
+ if (mSsidList != null) {
+ XmlUtil.writeNextValue(out, XML_TAG_SSID_LIST, mSsidList);
+ }
+ }
+
+ @Override
+ public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
+ throws XmlPullParserException, IOException {
+ if (shared) {
+ throw new XmlPullParserException("Share data not supported");
+ }
+
+ 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_SSID_LIST:
+ mSsidList = (Set<String>) value;
+ break;
+ default:
+ throw new XmlPullParserException("Unknown tag under "
+ + XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST
+ + ": " + valueName[0]);
+ }
+ }
+ }
+
+ @Override
+ public void resetData(boolean shared) {
+ if (!shared) {
+ mSsidList = null;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return XML_TAG_SECTION_HEADER_DELETED_EPHEMERAL_SSID_LIST;
+ }
+
+ @Override
+ public boolean supportShareData() {
+ return false;
+ }
+
+ /**
+ * An empty set will be returned for null SSID list.
+ *
+ * @return Set of SSIDs
+ */
+ public Set<String> getSsidList() {
+ if (mSsidList == null) {
+ return new HashSet<String>();
+ }
+ return mSsidList;
+ }
+
+ public void setSsidList(Set<String> ssidList) {
+ mSsidList = ssidList;
+ }
+}
+
diff --git a/service/java/com/android/server/wifi/DummyLogMessage.java b/service/java/com/android/server/wifi/DummyLogMessage.java
new file mode 100644
index 0000000..7eafcc7
--- /dev/null
+++ b/service/java/com/android/server/wifi/DummyLogMessage.java
@@ -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.
+ */
+
+package com.android.server.wifi;
+
+/** LogMessage implementation that does nothing. */
+public class DummyLogMessage implements WifiLog.LogMessage {
+ @Override
+ public WifiLog.LogMessage r(String value) {
+ return this;
+ }
+
+ @Override
+ public WifiLog.LogMessage c(String value) {
+ return this;
+ }
+
+ @Override
+ public WifiLog.LogMessage c(long value) {
+ return this;
+ }
+
+ @Override
+ public WifiLog.LogMessage c(char value) {
+ return this;
+ }
+
+ @Override
+ public WifiLog.LogMessage c(boolean value) {
+ return this;
+ }
+
+ @Override
+ public void flush() {
+ // Nothing to do.
+ }
+}
diff --git a/service/java/com/android/server/wifi/FakeWifiLog.java b/service/java/com/android/server/wifi/FakeWifiLog.java
new file mode 100644
index 0000000..a58d77f
--- /dev/null
+++ b/service/java/com/android/server/wifi/FakeWifiLog.java
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+/** WifiLog implementation that does nothing. */
+public class FakeWifiLog implements WifiLog {
+ private static final DummyLogMessage sDummyLogMessage = new DummyLogMessage();
+
+ // New-style methods.
+ @Override
+ public LogMessage err(String format) {
+ return sDummyLogMessage;
+ }
+
+ @Override
+ public LogMessage warn(String format) {
+ return sDummyLogMessage;
+ }
+
+ @Override
+ public LogMessage info(String format) {
+ return sDummyLogMessage;
+ }
+
+ @Override
+ public LogMessage trace(String format) {
+ return sDummyLogMessage;
+ }
+
+ @Override
+ public LogMessage dump(String format) {
+ return sDummyLogMessage;
+ }
+
+ @Override
+ public void eC(String msg) {
+ // Do nothing.
+ }
+
+ @Override
+ public void wC(String msg) {
+ // Do nothing.
+ }
+
+ @Override
+ public void iC(String msg) {
+ // Do nothing.
+ }
+
+ @Override
+ public void tC(String msg) {
+ // Do nothing.
+ }
+
+ // Legacy methods.
+ @Override
+ public void e(String msg) {
+ // Do nothing.
+ }
+
+ @Override
+ public void w(String msg) {
+ // Do nothing.
+ }
+
+ @Override
+ public void i(String msg) {
+ // Do nothing.
+ }
+
+ @Override
+ public void d(String msg) {
+ // Do nothing.
+ }
+
+ @Override
+ public void v(String msg) {
+ // Do nothing.
+ }
+}
diff --git a/service/java/com/android/server/wifi/FrameworkFacade.java b/service/java/com/android/server/wifi/FrameworkFacade.java
index cb03f7a..ba114df 100644
--- a/service/java/com/android/server/wifi/FrameworkFacade.java
+++ b/service/java/com/android/server/wifi/FrameworkFacade.java
@@ -16,27 +16,26 @@
package com.android.server.wifi;
+import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.net.ConnectivityManager;
+import android.database.ContentObserver;
import android.net.TrafficStats;
+import android.net.Uri;
import android.net.ip.IpManager;
-import android.net.wifi.IWifiScanner;
-import android.net.wifi.WifiScanner;
+import android.os.BatteryStats;
import android.os.Handler;
import android.os.IBinder;
-import android.os.INetworkManagementService;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.provider.Settings;
-import android.security.KeyStore;
import android.telephony.CarrierConfigManager;
-import java.util.ArrayList;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.wifi.util.WifiAsyncChannel;
/**
* This class allows overriding objects with mocks to write unit tests
@@ -44,16 +43,6 @@
public class FrameworkFacade {
public static final String TAG = "FrameworkFacade";
- public BaseWifiLogger makeBaseLogger() {
- return new BaseWifiLogger();
- }
-
- public BaseWifiLogger makeRealLogger(
- Context context, WifiStateMachine stateMachine, WifiNative wifiNative,
- BuildProperties buildProperties) {
- return new WifiLogger(context, stateMachine, wifiNative, buildProperties);
- }
-
public boolean setIntegerSetting(Context context, String name, int def) {
return Settings.Global.putInt(context.getContentResolver(), name, def);
}
@@ -74,13 +63,31 @@
return Settings.Global.getString(context.getContentResolver(), name);
}
+ /**
+ * Helper method for classes to register a ContentObserver
+ * {@see ContentResolver#registerContentObserver(Uri,boolean,ContentObserver)}.
+ *
+ * @param context
+ * @param uri
+ * @param notifyForDescendants
+ * @param contentObserver
+ */
+ public void registerContentObserver(Context context, Uri uri,
+ boolean notifyForDescendants, ContentObserver contentObserver) {
+ context.getContentResolver().registerContentObserver(uri, notifyForDescendants,
+ contentObserver);
+ }
+
public IBinder getService(String serviceName) {
return ServiceManager.getService(serviceName);
}
- public WifiScanner makeWifiScanner(Context context, Looper looper) {
- return new WifiScanner(context, IWifiScanner.Stub.asInterface(
- getService(Context.WIFI_SCANNING_SERVICE)), looper);
+ /**
+ * Returns the battery stats interface
+ * @return IBatteryStats BatteryStats service interface
+ */
+ public IBatteryStats getBatteryService() {
+ return IBatteryStats.Stub.asInterface(getService(BatteryStats.SERVICE_NAME));
}
public PendingIntent getBroadcast(Context context, int requestCode, Intent intent, int flags) {
@@ -89,7 +96,7 @@
public SupplicantStateTracker makeSupplicantStateTracker(Context context,
WifiConfigManager configManager, Handler handler) {
- return new SupplicantStateTracker(context, configManager, handler);
+ return new SupplicantStateTracker(context, configManager, this, handler);
}
public boolean getConfigWiFiDisableInECBM(Context context) {
@@ -128,28 +135,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
@@ -160,10 +145,31 @@
return AppGlobals.getPackageManager().checkUidPermission(permName, uid);
}
- public WifiConfigManager makeWifiConfigManager(Context context, WifiNative wifiNative,
- FrameworkFacade frameworkFacade, Clock clock, UserManager userManager,
- KeyStore keyStore) {
- return new WifiConfigManager(context, wifiNative, frameworkFacade, clock, userManager,
- keyStore);
+ /**
+ * Create a new instance of WifiAsyncChannel
+ * @param tag String corresponding to the service creating the channel
+ * @return WifiAsyncChannel object created
+ */
+ public WifiAsyncChannel makeWifiAsyncChannel(String tag) {
+ return new WifiAsyncChannel(tag);
+ }
+
+ /**
+ * Check if the device will be restarting after decrypting during boot by calling {@link
+ * StorageManager.inCryptKeeperBounce}.
+ * @return true if the device will restart, false otherwise
+ */
+ public boolean inStorageManagerCryptKeeperBounce() {
+ return StorageManager.inCryptKeeperBounce();
+ }
+
+ /**
+ * Check if the provided uid is the app in the foreground.
+ * @param uid the uid to check
+ * @return true if the app is in the foreground, false otherwise
+ * @throws RemoteException
+ */
+ public boolean isAppForeground(int uid) throws RemoteException {
+ return ActivityManager.getService().isAppForeground(uid);
}
}
diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java
new file mode 100644
index 0000000..a5e1273
--- /dev/null
+++ b/service/java/com/android/server/wifi/HalDeviceManager.java
@@ -0,0 +1,1950 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.hardware.wifi.V1_0.IWifi;
+import android.hardware.wifi.V1_0.IWifiApIface;
+import android.hardware.wifi.V1_0.IWifiChip;
+import android.hardware.wifi.V1_0.IWifiChipEventCallback;
+import android.hardware.wifi.V1_0.IWifiEventCallback;
+import android.hardware.wifi.V1_0.IWifiIface;
+import android.hardware.wifi.V1_0.IWifiNanIface;
+import android.hardware.wifi.V1_0.IWifiP2pIface;
+import android.hardware.wifi.V1_0.IWifiRttController;
+import android.hardware.wifi.V1_0.IWifiStaIface;
+import android.hardware.wifi.V1_0.IfaceType;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferStatus;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.Handler;
+import android.os.HwRemoteBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.MutableBoolean;
+import android.util.MutableInt;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Handles device management through the HAL (HIDL) interface.
+ */
+public class HalDeviceManager {
+ private static final String TAG = "HalDeviceManager";
+ private static final boolean DBG = false;
+
+ private static final int START_HAL_RETRY_INTERVAL_MS = 20;
+ // Number of attempts a start() is re-tried. A value of 0 means no retries after a single
+ // attempt.
+ @VisibleForTesting
+ public static final int START_HAL_RETRY_TIMES = 3;
+ @VisibleForTesting
+ public static final String HAL_INSTANCE_NAME = "default";
+
+ // public API
+ public HalDeviceManager() {
+ mInterfaceAvailableForRequestListeners.put(IfaceType.STA, new HashSet<>());
+ mInterfaceAvailableForRequestListeners.put(IfaceType.AP, new HashSet<>());
+ mInterfaceAvailableForRequestListeners.put(IfaceType.P2P, new HashSet<>());
+ mInterfaceAvailableForRequestListeners.put(IfaceType.NAN, new HashSet<>());
+ }
+
+ /**
+ * Actually starts the HalDeviceManager: separate from constructor since may want to phase
+ * at a later time.
+ *
+ * TODO: if decide that no need for separating construction from initialization (e.g. both are
+ * done at injector) then move to constructor.
+ */
+ public void initialize() {
+ initializeInternal();
+ }
+
+ /**
+ * Register a ManagerStatusListener to get information about the status of the manager. Use the
+ * isReady() and isStarted() methods to check status immediately after registration and when
+ * triggered.
+ *
+ * It is safe to re-register the same callback object - duplicates are detected and only a
+ * single copy kept.
+ *
+ * @param listener ManagerStatusListener listener object.
+ * @param looper Looper on which to dispatch listener. Null implies current looper.
+ */
+ public void registerStatusListener(ManagerStatusListener listener, Looper looper) {
+ synchronized (mLock) {
+ if (!mManagerStatusListeners.add(new ManagerStatusListenerProxy(listener,
+ looper == null ? Looper.myLooper() : looper))) {
+ Log.w(TAG, "registerStatusListener: duplicate registration ignored");
+ }
+ }
+ }
+
+ /**
+ * Returns whether the vendor HAL is supported on this device or not.
+ */
+ public boolean isSupported() {
+ return isSupportedInternal();
+ }
+
+ /**
+ * Returns the current status of the HalDeviceManager: whether or not it is ready to execute
+ * commands. A return of 'false' indicates that the HAL service (IWifi) is not available. Use
+ * the registerStatusListener() to listener for status changes.
+ */
+ public boolean isReady() {
+ return mWifi != null;
+ }
+
+ /**
+ * Returns the current status of Wi-Fi: started (true) or stopped (false).
+ *
+ * Note: direct call to HIDL.
+ */
+ public boolean isStarted() {
+ return isWifiStarted();
+ }
+
+ /**
+ * Attempts to start Wi-Fi (using HIDL). Returns the success (true) or failure (false) or
+ * the start operation. Will also dispatch any registered ManagerStatusCallback.onStart() on
+ * success.
+ *
+ * Note: direct call to HIDL.
+ */
+ public boolean start() {
+ return startWifi();
+ }
+
+ /**
+ * Stops Wi-Fi. Will also dispatch any registeredManagerStatusCallback.onStop().
+ *
+ * Note: direct call to HIDL - failure is not-expected.
+ */
+ public void stop() {
+ stopWifi();
+ }
+
+ /**
+ * HAL device manager status change listener.
+ */
+ public interface ManagerStatusListener {
+ /**
+ * Indicates that the status of the HalDeviceManager has changed. Use isReady() and
+ * isStarted() to obtain status information.
+ */
+ void onStatusChanged();
+ }
+
+ /**
+ * Return the set of supported interface types across all Wi-Fi chips on the device.
+ *
+ * @return A set of IfaceTypes constants (possibly empty, e.g. on error).
+ */
+ Set<Integer> getSupportedIfaceTypes() {
+ return getSupportedIfaceTypesInternal(null);
+ }
+
+ /**
+ * Return the set of supported interface types for the specified Wi-Fi chip.
+ *
+ * @return A set of IfaceTypes constants (possibly empty, e.g. on error).
+ */
+ Set<Integer> getSupportedIfaceTypes(IWifiChip chip) {
+ return getSupportedIfaceTypesInternal(chip);
+ }
+
+ // interface-specific behavior
+
+ /**
+ * Create a STA interface if possible. Changes chip mode and removes conflicting interfaces if
+ * needed and permitted by priority.
+ *
+ * @param destroyedListener Optional (nullable) listener to call when the allocated interface
+ * is removed. Will only be registered and used if an interface is
+ * created successfully.
+ * @param looper The looper on which to dispatch the listener. A null value indicates the
+ * current thread.
+ * @return A newly created interface - or null if the interface could not be created.
+ */
+ public IWifiStaIface createStaIface(InterfaceDestroyedListener destroyedListener,
+ Looper looper) {
+ return (IWifiStaIface) createIface(IfaceType.STA, destroyedListener, looper);
+ }
+
+ /**
+ * Create AP interface if possible (see createStaIface doc).
+ */
+ public IWifiApIface createApIface(InterfaceDestroyedListener destroyedListener,
+ Looper looper) {
+ return (IWifiApIface) createIface(IfaceType.AP, destroyedListener, looper);
+ }
+
+ /**
+ * Create P2P interface if possible (see createStaIface doc).
+ */
+ public IWifiP2pIface createP2pIface(InterfaceDestroyedListener destroyedListener,
+ Looper looper) {
+ return (IWifiP2pIface) createIface(IfaceType.P2P, destroyedListener, looper);
+ }
+
+ /**
+ * Create NAN interface if possible (see createStaIface doc).
+ */
+ public IWifiNanIface createNanIface(InterfaceDestroyedListener destroyedListener,
+ Looper looper) {
+ return (IWifiNanIface) createIface(IfaceType.NAN, destroyedListener, looper);
+ }
+
+ /**
+ * Removes (releases/destroys) the given interface. Will trigger any registered
+ * InterfaceDestroyedListeners and possibly some InterfaceAvailableForRequestListeners if we
+ * can potentially create some other interfaces as a result of removing this interface.
+ */
+ public boolean removeIface(IWifiIface iface) {
+ boolean success = removeIfaceInternal(iface);
+ dispatchAvailableForRequestListeners();
+ return success;
+ }
+
+ /**
+ * Returns the IWifiChip corresponding to the specified interface (or null on error).
+ *
+ * Note: clients must not perform chip mode changes or interface management (create/delete)
+ * operations on IWifiChip directly. However, they can use the IWifiChip interface to perform
+ * other functions - e.g. calling the debug/trace methods.
+ */
+ public IWifiChip getChip(IWifiIface iface) {
+ if (DBG) Log.d(TAG, "getChip: iface(name)=" + getName(iface));
+
+ synchronized (mLock) {
+ InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(iface);
+ if (cacheEntry == null) {
+ Log.e(TAG, "getChip: no entry for iface(name)=" + getName(iface));
+ return null;
+ }
+
+ return cacheEntry.chip;
+ }
+ }
+
+ /**
+ * Register an InterfaceDestroyedListener to the specified iface - returns true on success
+ * and false on failure. This listener is in addition to the one registered when the interface
+ * was created - allowing non-creators to monitor interface status.
+ *
+ * Listener called-back on the specified looper - or on the current looper if a null is passed.
+ */
+ public boolean registerDestroyedListener(IWifiIface iface,
+ InterfaceDestroyedListener destroyedListener,
+ Looper looper) {
+ if (DBG) Log.d(TAG, "registerDestroyedListener: iface(name)=" + getName(iface));
+
+ synchronized (mLock) {
+ InterfaceCacheEntry cacheEntry = mInterfaceInfoCache.get(iface);
+ if (cacheEntry == null) {
+ Log.e(TAG, "registerDestroyedListener: no entry for iface(name)="
+ + getName(iface));
+ return false;
+ }
+
+ return cacheEntry.destroyedListeners.add(
+ new InterfaceDestroyedListenerProxy(destroyedListener,
+ looper == null ? Looper.myLooper() : looper));
+ }
+ }
+
+ /**
+ * Register a listener to be called when an interface of the specified type could be requested.
+ * No guarantees are provided (some other entity could request it first). The listener is
+ * active from registration until unregistration - using
+ * unregisterInterfaceAvailableForRequestListener().
+ *
+ * Only a single instance of a listener will be registered (even if the specified looper is
+ * different).
+ *
+ * Note that if it is possible to create the specified interface type at registration time
+ * then the callback will be triggered immediately.
+ *
+ * @param ifaceType The interface type (IfaceType) to be monitored.
+ * @param listener Listener to call when an interface of the requested
+ * type could be created
+ * @param looper The looper on which to dispatch the listener. A null value indicates the
+ * current thread.
+ */
+ public void registerInterfaceAvailableForRequestListener(int ifaceType,
+ InterfaceAvailableForRequestListener listener, Looper looper) {
+ mInterfaceAvailableForRequestListeners.get(ifaceType).add(
+ new InterfaceAvailableForRequestListenerProxy(listener,
+ looper == null ? Looper.myLooper() : looper));
+
+ WifiChipInfo[] chipInfos = getAllChipInfo();
+ if (chipInfos == null) {
+ Log.e(TAG,
+ "registerInterfaceAvailableForRequestListener: no chip info found - but "
+ + "possibly registered pre-started - ignoring");
+ return;
+ }
+ dispatchAvailableForRequestListenersForType(ifaceType, chipInfos);
+ }
+
+ /**
+ * Unregisters a listener registered with registerInterfaceAvailableForRequestListener().
+ */
+ public void unregisterInterfaceAvailableForRequestListener(
+ int ifaceType,
+ InterfaceAvailableForRequestListener listener) {
+ Iterator<InterfaceAvailableForRequestListenerProxy> it =
+ mInterfaceAvailableForRequestListeners.get(ifaceType).iterator();
+ while (it.hasNext()) {
+ if (it.next().mListener == listener) {
+ it.remove();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Return the name of the input interface or null on error.
+ */
+ public static String getName(IWifiIface iface) {
+ if (iface == null) {
+ return "<null>";
+ }
+
+ Mutable<String> nameResp = new Mutable<>();
+ try {
+ iface.getName((WifiStatus status, String name) -> {
+ if (status.code == WifiStatusCode.SUCCESS) {
+ nameResp.value = name;
+ } else {
+ Log.e(TAG, "Error on getName: " + statusString(status));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception on getName: " + e);
+ }
+
+ return nameResp.value;
+ }
+
+ /**
+ * Called when interface is destroyed.
+ */
+ public interface InterfaceDestroyedListener {
+ /**
+ * Called for every interface on which registered when destroyed - whether
+ * destroyed by releaseIface() or through chip mode change or through Wi-Fi
+ * going down.
+ *
+ * Can be registered when the interface is requested with createXxxIface() - will
+ * only be valid if the interface creation was successful - i.e. a non-null was returned.
+ */
+ void onDestroyed();
+ }
+
+ /**
+ * Called when an interface type is possibly available for creation.
+ */
+ public interface InterfaceAvailableForRequestListener {
+ /**
+ * Registered when an interface type could be requested. Registered with
+ * registerInterfaceAvailableForRequestListener() and unregistered with
+ * unregisterInterfaceAvailableForRequestListener().
+ */
+ void onAvailableForRequest();
+ }
+
+ /**
+ * Creates a IWifiRttController corresponding to the input interface. A direct match to the
+ * IWifiChip.createRttController() method.
+ *
+ * Returns the created IWifiRttController or a null on error.
+ */
+ public IWifiRttController createRttController(IWifiIface boundIface) {
+ if (DBG) Log.d(TAG, "createRttController: boundIface(name)=" + getName(boundIface));
+ synchronized (mLock) {
+ if (mWifi == null) {
+ Log.e(TAG, "createRttController: null IWifi -- boundIface(name)="
+ + getName(boundIface));
+ return null;
+ }
+
+ IWifiChip chip = getChip(boundIface);
+ if (chip == null) {
+ Log.e(TAG, "createRttController: null IWifiChip -- boundIface(name)="
+ + getName(boundIface));
+ return null;
+ }
+
+ Mutable<IWifiRttController> rttResp = new Mutable<>();
+ try {
+ chip.createRttController(boundIface,
+ (WifiStatus status, IWifiRttController rtt) -> {
+ if (status.code == WifiStatusCode.SUCCESS) {
+ rttResp.value = rtt;
+ } else {
+ Log.e(TAG, "IWifiChip.createRttController failed: " + statusString(
+ status));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "IWifiChip.createRttController exception: " + e);
+ }
+
+ return rttResp.value;
+ }
+ }
+
+ // internal state
+
+ /* This "PRIORITY" is not for deciding interface elimination (that is controlled by
+ * allowedToDeleteIfaceTypeForRequestedType. This priority is used for:
+ * - Comparing 2 configuration options
+ * - Order of dispatch of available for request listeners
+ */
+ private static final int[] IFACE_TYPES_BY_PRIORITY =
+ {IfaceType.AP, IfaceType.STA, IfaceType.P2P, IfaceType.NAN};
+
+ private final Object mLock = new Object();
+
+ private IServiceManager mServiceManager;
+ private IWifi mWifi;
+ private final WifiEventCallback mWifiEventCallback = new WifiEventCallback();
+ private final Set<ManagerStatusListenerProxy> mManagerStatusListeners = new HashSet<>();
+ private final SparseArray<Set<InterfaceAvailableForRequestListenerProxy>>
+ mInterfaceAvailableForRequestListeners = new SparseArray<>();
+
+ /*
+ * This is the only place where we cache HIDL information in this manager. Necessary since
+ * we need to keep a list of registered destroyed listeners. Will be validated regularly
+ * in getAllChipInfoAndValidateCache().
+ */
+ private final Map<IWifiIface, InterfaceCacheEntry> mInterfaceInfoCache = new HashMap<>();
+
+ private class InterfaceCacheEntry {
+ public IWifiChip chip;
+ public int chipId;
+ public String name;
+ public int type;
+ public Set<InterfaceDestroyedListenerProxy> destroyedListeners = new HashSet<>();
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{name=").append(name).append(", type=").append(type)
+ .append(", destroyedListeners.size()=").append(destroyedListeners.size())
+ .append("}");
+ return sb.toString();
+ }
+ }
+
+ private class WifiIfaceInfo {
+ public String name;
+ public IWifiIface iface;
+ }
+
+ private class WifiChipInfo {
+ public IWifiChip chip;
+ public int chipId;
+ public ArrayList<IWifiChip.ChipMode> availableModes;
+ public boolean currentModeIdValid;
+ public int currentModeId;
+ public WifiIfaceInfo[][] ifaces = new WifiIfaceInfo[IFACE_TYPES_BY_PRIORITY.length][];
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{chipId=").append(chipId).append(", availableModes=").append(availableModes)
+ .append(", currentModeIdValid=").append(currentModeIdValid)
+ .append(", currentModeId=").append(currentModeId);
+ for (int type: IFACE_TYPES_BY_PRIORITY) {
+ sb.append(", ifaces[" + type + "].length=").append(ifaces[type].length);
+ }
+ sb.append(")");
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Wrapper function to access the HIDL services. Created to be mockable in unit-tests.
+ */
+ protected IWifi getWifiServiceMockable() {
+ try {
+ return IWifi.getService();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception getting IWifi service: " + e);
+ return null;
+ }
+ }
+
+ protected IServiceManager getServiceManagerMockable() {
+ try {
+ return IServiceManager.getService();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception getting IServiceManager: " + e);
+ return null;
+ }
+ }
+
+ // internal implementation
+
+ private void initializeInternal() {
+ initIServiceManagerIfNecessary();
+ }
+
+ private void teardownInternal() {
+ managerStatusListenerDispatch();
+ dispatchAllDestroyedListeners();
+ mInterfaceAvailableForRequestListeners.get(IfaceType.STA).clear();
+ mInterfaceAvailableForRequestListeners.get(IfaceType.AP).clear();
+ mInterfaceAvailableForRequestListeners.get(IfaceType.P2P).clear();
+ mInterfaceAvailableForRequestListeners.get(IfaceType.NAN).clear();
+ }
+
+ private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
+ cookie -> {
+ Log.wtf(TAG, "IServiceManager died: cookie=" + cookie);
+ synchronized (mLock) {
+ mServiceManager = null;
+ // theoretically can call initServiceManager again here - but
+ // there's no point since most likely system is going to reboot
+ }
+ };
+
+ private final IServiceNotification mServiceNotificationCallback =
+ new IServiceNotification.Stub() {
+ @Override
+ public void onRegistration(String fqName, String name,
+ boolean preexisting) {
+ Log.d(TAG, "IWifi registration notification: fqName=" + fqName
+ + ", name=" + name + ", preexisting=" + preexisting);
+ mWifi = null; // get rid of old copy!
+ initIWifiIfNecessary();
+ stopWifi(); // just in case
+ }
+ };
+
+ /**
+ * Failures of IServiceManager are most likely system breaking in any case. Behavior here
+ * will be to WTF and continue.
+ */
+ private void initIServiceManagerIfNecessary() {
+ if (DBG) Log.d(TAG, "initIServiceManagerIfNecessary");
+
+ synchronized (mLock) {
+ if (mServiceManager != null) {
+ return;
+ }
+
+ mServiceManager = getServiceManagerMockable();
+ if (mServiceManager == null) {
+ Log.wtf(TAG, "Failed to get IServiceManager instance");
+ } else {
+ try {
+ if (!mServiceManager.linkToDeath(
+ mServiceManagerDeathRecipient, /* don't care */ 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
+ mServiceManager = null;
+ return;
+ }
+
+ if (!mServiceManager.registerForNotifications(IWifi.kInterfaceName, "",
+ mServiceNotificationCallback)) {
+ Log.wtf(TAG, "Failed to register a listener for IWifi service");
+ mServiceManager = null;
+ }
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
+ mServiceManager = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * Uses the IServiceManager to query if the vendor HAL is present in the VINTF for the device
+ * or not.
+ * @return true if supported, false otherwise.
+ */
+ private boolean isSupportedInternal() {
+ if (DBG) Log.d(TAG, "isSupportedInternal");
+
+ synchronized (mLock) {
+ if (mServiceManager == null) {
+ Log.e(TAG, "isSupported: called but mServiceManager is null!?");
+ return false;
+ }
+ try {
+ return (mServiceManager.getTransport(IWifi.kInterfaceName, HAL_INSTANCE_NAME)
+ != IServiceManager.Transport.EMPTY);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Exception while operating on IServiceManager: " + e);
+ return false;
+ }
+ }
+ }
+
+ private final HwRemoteBinder.DeathRecipient mIWifiDeathRecipient =
+ cookie -> {
+ Log.e(TAG, "IWifi HAL service died! Have a listener for it ... cookie=" + cookie);
+ synchronized (mLock) { // prevents race condition with surrounding method
+ mWifi = null;
+ teardownInternal();
+ // don't restart: wait for registration notification
+ }
+ };
+
+ /**
+ * Initialize IWifi and register death listener and event callback.
+ *
+ * - It is possible that IWifi is not ready - we have a listener on IServiceManager for it.
+ * - It is not expected that any of the registrations will fail. Possible indication that
+ * service died after we obtained a handle to it.
+ *
+ * Here and elsewhere we assume that death listener will do the right thing!
+ */
+ private void initIWifiIfNecessary() {
+ if (DBG) Log.d(TAG, "initIWifiIfNecessary");
+
+ synchronized (mLock) {
+ if (mWifi != null) {
+ return;
+ }
+
+ try {
+ mWifi = getWifiServiceMockable();
+ if (mWifi == null) {
+ Log.e(TAG, "IWifi not (yet) available - but have a listener for it ...");
+ return;
+ }
+
+ if (!mWifi.linkToDeath(mIWifiDeathRecipient, /* don't care */ 0)) {
+ Log.e(TAG, "Error on linkToDeath on IWifi - will retry later");
+ return;
+ }
+
+ WifiStatus status = mWifi.registerEventCallback(mWifiEventCallback);
+ if (status.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG, "IWifi.registerEventCallback failed: " + statusString(status));
+ mWifi = null;
+ return;
+ }
+ managerStatusListenerDispatch();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while operating on IWifi: " + e);
+ }
+ }
+ }
+
+ /**
+ * Registers event listeners on all IWifiChips after a successful start: DEBUG only!
+ *
+ * We don't need the listeners since any callbacks are just confirmation of status codes we
+ * obtain directly from mode changes or interface creation/deletion.
+ *
+ * Relies (to the degree we care) on the service removing all listeners when Wi-Fi is stopped.
+ */
+ private void initIWifiChipDebugListeners() {
+ if (DBG) Log.d(TAG, "initIWifiChipDebugListeners");
+
+ if (!DBG) {
+ return;
+ }
+
+ synchronized (mLock) {
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ Mutable<ArrayList<Integer>> chipIdsResp = new Mutable<>();
+
+ // get all chip IDs
+ mWifi.getChipIds((WifiStatus status, ArrayList<Integer> chipIds) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ chipIdsResp.value = chipIds;
+ } else {
+ Log.e(TAG, "getChipIds failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return;
+ }
+
+ if (DBG) Log.d(TAG, "getChipIds=" + chipIdsResp.value);
+ if (chipIdsResp.value.size() == 0) {
+ Log.e(TAG, "Should have at least 1 chip!");
+ return;
+ }
+
+ // register a callback for each chip
+ Mutable<IWifiChip> chipResp = new Mutable<>();
+ for (Integer chipId: chipIdsResp.value) {
+ mWifi.getChip(chipId, (WifiStatus status, IWifiChip chip) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ chipResp.value = chip;
+ } else {
+ Log.e(TAG, "getChip failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ continue; // still try next one?
+ }
+
+ WifiStatus status = chipResp.value.registerEventCallback(
+ new IWifiChipEventCallback.Stub() {
+ @Override
+ public void onChipReconfigured(int modeId) throws RemoteException {
+ Log.d(TAG, "onChipReconfigured: modeId=" + modeId);
+ }
+
+ @Override
+ public void onChipReconfigureFailure(WifiStatus status)
+ throws RemoteException {
+ Log.d(TAG, "onChipReconfigureFailure: status=" + statusString(
+ status));
+ }
+
+ @Override
+ public void onIfaceAdded(int type, String name)
+ throws RemoteException {
+ Log.d(TAG, "onIfaceAdded: type=" + type + ", name=" + name);
+ }
+
+ @Override
+ public void onIfaceRemoved(int type, String name)
+ throws RemoteException {
+ Log.d(TAG, "onIfaceRemoved: type=" + type + ", name=" + name);
+ }
+
+ @Override
+ public void onDebugRingBufferDataAvailable(
+ WifiDebugRingBufferStatus status,
+ ArrayList<Byte> data) throws RemoteException {
+ Log.d(TAG, "onDebugRingBufferDataAvailable");
+ }
+
+ @Override
+ public void onDebugErrorAlert(int errorCode,
+ ArrayList<Byte> debugData)
+ throws RemoteException {
+ Log.d(TAG, "onDebugErrorAlert");
+ }
+ });
+ if (status.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG, "registerEventCallback failed: " + statusString(status));
+ continue; // still try next one?
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "initIWifiChipDebugListeners: exception: " + e);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Get current information about all the chips in the system: modes, current mode (if any), and
+ * any existing interfaces.
+ *
+ * Intended to be called whenever we need to configure the chips - information is NOT cached (to
+ * reduce the likelihood that we get out-of-sync).
+ */
+ private WifiChipInfo[] getAllChipInfo() {
+ if (DBG) Log.d(TAG, "getAllChipInfo");
+
+ synchronized (mLock) {
+ if (mWifi == null) {
+ Log.e(TAG, "getAllChipInfo: called but mWifi is null!?");
+ return null;
+ }
+
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ Mutable<ArrayList<Integer>> chipIdsResp = new Mutable<>();
+
+ // get all chip IDs
+ mWifi.getChipIds((WifiStatus status, ArrayList<Integer> chipIds) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ chipIdsResp.value = chipIds;
+ } else {
+ Log.e(TAG, "getChipIds failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+
+ if (DBG) Log.d(TAG, "getChipIds=" + chipIdsResp.value);
+ if (chipIdsResp.value.size() == 0) {
+ Log.e(TAG, "Should have at least 1 chip!");
+ return null;
+ }
+
+ int chipInfoIndex = 0;
+ WifiChipInfo[] chipsInfo = new WifiChipInfo[chipIdsResp.value.size()];
+
+ Mutable<IWifiChip> chipResp = new Mutable<>();
+ for (Integer chipId: chipIdsResp.value) {
+ mWifi.getChip(chipId, (WifiStatus status, IWifiChip chip) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ chipResp.value = chip;
+ } else {
+ Log.e(TAG, "getChip failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+
+ Mutable<ArrayList<IWifiChip.ChipMode>> availableModesResp = new Mutable<>();
+ chipResp.value.getAvailableModes(
+ (WifiStatus status, ArrayList<IWifiChip.ChipMode> modes) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ availableModesResp.value = modes;
+ } else {
+ Log.e(TAG, "getAvailableModes failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+
+ MutableBoolean currentModeValidResp = new MutableBoolean(false);
+ MutableInt currentModeResp = new MutableInt(0);
+ chipResp.value.getMode((WifiStatus status, int modeId) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ currentModeValidResp.value = true;
+ currentModeResp.value = modeId;
+ } else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {
+ statusOk.value = true; // valid response
+ } else {
+ Log.e(TAG, "getMode failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+
+ Mutable<ArrayList<String>> ifaceNamesResp = new Mutable<>();
+ MutableInt ifaceIndex = new MutableInt(0);
+
+ chipResp.value.getStaIfaceNames(
+ (WifiStatus status, ArrayList<String> ifnames) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ ifaceNamesResp.value = ifnames;
+ } else {
+ Log.e(TAG, "getStaIfaceNames failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+
+ WifiIfaceInfo[] staIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
+ for (String ifaceName: ifaceNamesResp.value) {
+ chipResp.value.getStaIface(ifaceName,
+ (WifiStatus status, IWifiStaIface iface) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
+ ifaceInfo.name = ifaceName;
+ ifaceInfo.iface = iface;
+ staIfaces[ifaceIndex.value++] = ifaceInfo;
+ } else {
+ Log.e(TAG, "getStaIface failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+ }
+
+ ifaceIndex.value = 0;
+ chipResp.value.getApIfaceNames(
+ (WifiStatus status, ArrayList<String> ifnames) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ ifaceNamesResp.value = ifnames;
+ } else {
+ Log.e(TAG, "getApIfaceNames failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+
+ WifiIfaceInfo[] apIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
+ for (String ifaceName: ifaceNamesResp.value) {
+ chipResp.value.getApIface(ifaceName,
+ (WifiStatus status, IWifiApIface iface) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
+ ifaceInfo.name = ifaceName;
+ ifaceInfo.iface = iface;
+ apIfaces[ifaceIndex.value++] = ifaceInfo;
+ } else {
+ Log.e(TAG, "getApIface failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+ }
+
+ ifaceIndex.value = 0;
+ chipResp.value.getP2pIfaceNames(
+ (WifiStatus status, ArrayList<String> ifnames) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ ifaceNamesResp.value = ifnames;
+ } else {
+ Log.e(TAG, "getP2pIfaceNames failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+
+ WifiIfaceInfo[] p2pIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
+ for (String ifaceName: ifaceNamesResp.value) {
+ chipResp.value.getP2pIface(ifaceName,
+ (WifiStatus status, IWifiP2pIface iface) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
+ ifaceInfo.name = ifaceName;
+ ifaceInfo.iface = iface;
+ p2pIfaces[ifaceIndex.value++] = ifaceInfo;
+ } else {
+ Log.e(TAG, "getP2pIface failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+ }
+
+ ifaceIndex.value = 0;
+ chipResp.value.getNanIfaceNames(
+ (WifiStatus status, ArrayList<String> ifnames) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ ifaceNamesResp.value = ifnames;
+ } else {
+ Log.e(TAG, "getNanIfaceNames failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+
+ WifiIfaceInfo[] nanIfaces = new WifiIfaceInfo[ifaceNamesResp.value.size()];
+ for (String ifaceName: ifaceNamesResp.value) {
+ chipResp.value.getNanIface(ifaceName,
+ (WifiStatus status, IWifiNanIface iface) -> {
+ statusOk.value = status.code == WifiStatusCode.SUCCESS;
+ if (statusOk.value) {
+ WifiIfaceInfo ifaceInfo = new WifiIfaceInfo();
+ ifaceInfo.name = ifaceName;
+ ifaceInfo.iface = iface;
+ nanIfaces[ifaceIndex.value++] = ifaceInfo;
+ } else {
+ Log.e(TAG, "getNanIface failed: " + statusString(status));
+ }
+ });
+ if (!statusOk.value) {
+ return null;
+ }
+ }
+
+ WifiChipInfo chipInfo = new WifiChipInfo();
+ chipsInfo[chipInfoIndex++] = chipInfo;
+
+ chipInfo.chip = chipResp.value;
+ chipInfo.chipId = chipId;
+ chipInfo.availableModes = availableModesResp.value;
+ chipInfo.currentModeIdValid = currentModeValidResp.value;
+ chipInfo.currentModeId = currentModeResp.value;
+ chipInfo.ifaces[IfaceType.STA] = staIfaces;
+ chipInfo.ifaces[IfaceType.AP] = apIfaces;
+ chipInfo.ifaces[IfaceType.P2P] = p2pIfaces;
+ chipInfo.ifaces[IfaceType.NAN] = nanIfaces;
+ }
+
+ return chipsInfo;
+ } catch (RemoteException e) {
+ Log.e(TAG, "getAllChipInfoAndValidateCache exception: " + e);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks the local state of this object (the cached state) against the input 'chipInfos'
+ * state (which is a live representation of the Wi-Fi firmware status - read through the HAL).
+ * Returns 'true' if there are no discrepancies - 'false' otherwise.
+ *
+ * A discrepancy is if any local state contains references to a chip or interface which are not
+ * found on the information read from the chip.
+ */
+ private boolean validateInterfaceCache(WifiChipInfo[] chipInfos) {
+ if (DBG) Log.d(TAG, "validateInterfaceCache");
+
+ synchronized (mLock) {
+ for (Map.Entry<IWifiIface, InterfaceCacheEntry> entry: mInterfaceInfoCache.entrySet()) {
+ // search for chip
+ WifiChipInfo matchingChipInfo = null;
+ for (WifiChipInfo ci: chipInfos) {
+ if (ci.chipId == entry.getValue().chipId) {
+ matchingChipInfo = ci;
+ break;
+ }
+ }
+ if (matchingChipInfo == null) {
+ Log.e(TAG, "validateInterfaceCache: no chip found for " + entry.getValue());
+ return false;
+ }
+
+ // search for interface
+ WifiIfaceInfo[] ifaceInfoList = matchingChipInfo.ifaces[entry.getValue().type];
+ if (ifaceInfoList == null) {
+ Log.e(TAG, "validateInterfaceCache: invalid type on entry " + entry.getValue());
+ return false;
+ }
+
+ boolean matchFound = false;
+ for (WifiIfaceInfo ifaceInfo: ifaceInfoList) {
+ if (ifaceInfo.name.equals(entry.getValue().name)) {
+ matchFound = true;
+ break;
+ }
+ }
+ if (!matchFound) {
+ Log.e(TAG, "validateInterfaceCache: no interface found for "
+ + entry.getValue());
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private boolean isWifiStarted() {
+ if (DBG) Log.d(TAG, "isWifiStart");
+
+ synchronized (mLock) {
+ try {
+ if (mWifi == null) {
+ Log.w(TAG, "isWifiStarted called but mWifi is null!?");
+ return false;
+ } else {
+ return mWifi.isStarted();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "isWifiStarted exception: " + e);
+ return false;
+ }
+ }
+ }
+
+ private boolean startWifi() {
+ if (DBG) Log.d(TAG, "startWifi");
+
+ synchronized (mLock) {
+ try {
+ if (mWifi == null) {
+ Log.w(TAG, "startWifi called but mWifi is null!?");
+ return false;
+ } else {
+ int triedCount = 0;
+ while (triedCount <= START_HAL_RETRY_TIMES) {
+ WifiStatus status = mWifi.start();
+ if (status.code == WifiStatusCode.SUCCESS) {
+ initIWifiChipDebugListeners();
+ managerStatusListenerDispatch();
+ if (triedCount != 0) {
+ Log.d(TAG, "start IWifi succeeded after trying "
+ + triedCount + " times");
+ }
+ return true;
+ } else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) {
+ // Should retry. Hal might still be stopping.
+ Log.e(TAG, "Cannot start IWifi: " + statusString(status)
+ + ", Retrying...");
+ try {
+ Thread.sleep(START_HAL_RETRY_INTERVAL_MS);
+ } catch (InterruptedException ignore) {
+ // no-op
+ }
+ triedCount++;
+ } else {
+ // Should not retry on other failures.
+ Log.e(TAG, "Cannot start IWifi: " + statusString(status));
+ return false;
+ }
+ }
+ Log.e(TAG, "Cannot start IWifi after trying " + triedCount + " times");
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "startWifi exception: " + e);
+ return false;
+ }
+ }
+ }
+
+ private void stopWifi() {
+ if (DBG) Log.d(TAG, "stopWifi");
+
+ synchronized (mLock) {
+ try {
+ if (mWifi == null) {
+ Log.w(TAG, "stopWifi called but mWifi is null!?");
+ } else {
+ WifiStatus status = mWifi.stop();
+ if (status.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG, "Cannot stop IWifi: " + statusString(status));
+ }
+
+ // even on failure since WTF??
+ teardownInternal();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "stopWifi exception: " + e);
+ }
+ }
+ }
+
+ private class WifiEventCallback extends IWifiEventCallback.Stub {
+ @Override
+ public void onStart() throws RemoteException {
+ if (DBG) Log.d(TAG, "IWifiEventCallback.onStart");
+ // NOP: only happens in reaction to my calls - will handle directly
+ }
+
+ @Override
+ public void onStop() throws RemoteException {
+ if (DBG) Log.d(TAG, "IWifiEventCallback.onStop");
+ // NOP: only happens in reaction to my calls - will handle directly
+ }
+
+ @Override
+ public void onFailure(WifiStatus status) throws RemoteException {
+ Log.e(TAG, "IWifiEventCallback.onFailure: " + statusString(status));
+ teardownInternal();
+
+ // No need to do anything else: listeners may (will) re-start Wi-Fi
+ }
+ }
+
+ private void managerStatusListenerDispatch() {
+ synchronized (mLock) {
+ for (ManagerStatusListenerProxy cb : mManagerStatusListeners) {
+ cb.trigger();
+ }
+ }
+ }
+
+ private class ManagerStatusListenerProxy extends
+ ListenerProxy<ManagerStatusListener> {
+ ManagerStatusListenerProxy(ManagerStatusListener statusListener,
+ Looper looper) {
+ super(statusListener, looper, "ManagerStatusListenerProxy");
+ }
+
+ @Override
+ protected void action() {
+ mListener.onStatusChanged();
+ }
+ }
+
+ Set<Integer> getSupportedIfaceTypesInternal(IWifiChip chip) {
+ Set<Integer> results = new HashSet<>();
+
+ WifiChipInfo[] chipInfos = getAllChipInfo();
+ if (chipInfos == null) {
+ Log.e(TAG, "getSupportedIfaceTypesInternal: no chip info found");
+ return results;
+ }
+
+ MutableInt chipIdIfProvided = new MutableInt(0); // NOT using 0 as a magic value
+ if (chip != null) {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ try {
+ chip.getId((WifiStatus status, int id) -> {
+ if (status.code == WifiStatusCode.SUCCESS) {
+ chipIdIfProvided.value = id;
+ statusOk.value = true;
+ } else {
+ Log.e(TAG, "getSupportedIfaceTypesInternal: IWifiChip.getId() error: "
+ + statusString(status));
+ statusOk.value = false;
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "getSupportedIfaceTypesInternal IWifiChip.getId() exception: " + e);
+ return results;
+ }
+ if (!statusOk.value) {
+ return results;
+ }
+ }
+
+ for (WifiChipInfo wci: chipInfos) {
+ if (chip != null && wci.chipId != chipIdIfProvided.value) {
+ continue;
+ }
+
+ for (IWifiChip.ChipMode cm: wci.availableModes) {
+ for (IWifiChip.ChipIfaceCombination cic: cm.availableCombinations) {
+ for (IWifiChip.ChipIfaceCombinationLimit cicl: cic.limits) {
+ for (int type: cicl.types) {
+ results.add(type);
+ }
+ }
+ }
+ }
+ }
+
+ return results;
+ }
+
+ private IWifiIface createIface(int ifaceType, InterfaceDestroyedListener destroyedListener,
+ Looper looper) {
+ if (DBG) Log.d(TAG, "createIface: ifaceType=" + ifaceType);
+
+ synchronized (mLock) {
+ WifiChipInfo[] chipInfos = getAllChipInfo();
+ if (chipInfos == null) {
+ Log.e(TAG, "createIface: no chip info found");
+ stopWifi(); // major error: shutting down
+ return null;
+ }
+
+ if (!validateInterfaceCache(chipInfos)) {
+ Log.e(TAG, "createIface: local cache is invalid!");
+ stopWifi(); // major error: shutting down
+ return null;
+ }
+
+ IWifiIface iface = createIfaceIfPossible(chipInfos, ifaceType, destroyedListener,
+ looper);
+ if (iface != null) { // means that some configuration has changed
+ if (!dispatchAvailableForRequestListeners()) {
+ return null; // catastrophic failure - shut down
+ }
+ }
+
+ return iface;
+ }
+ }
+
+ private IWifiIface createIfaceIfPossible(WifiChipInfo[] chipInfos, int ifaceType,
+ InterfaceDestroyedListener destroyedListener, Looper looper) {
+ if (DBG) {
+ Log.d(TAG, "createIfaceIfPossible: chipInfos=" + Arrays.deepToString(chipInfos)
+ + ", ifaceType=" + ifaceType);
+ }
+ synchronized (mLock) {
+ IfaceCreationData bestIfaceCreationProposal = null;
+ for (WifiChipInfo chipInfo: chipInfos) {
+ for (IWifiChip.ChipMode chipMode: chipInfo.availableModes) {
+ for (IWifiChip.ChipIfaceCombination chipIfaceCombo : chipMode
+ .availableCombinations) {
+ int[][] expandedIfaceCombos = expandIfaceCombos(chipIfaceCombo);
+ if (DBG) {
+ Log.d(TAG, chipIfaceCombo + " expands to "
+ + Arrays.deepToString(expandedIfaceCombos));
+ }
+
+ for (int[] expandedIfaceCombo: expandedIfaceCombos) {
+ IfaceCreationData currentProposal = canIfaceComboSupportRequest(
+ chipInfo, chipMode, expandedIfaceCombo, ifaceType);
+ if (compareIfaceCreationData(currentProposal,
+ bestIfaceCreationProposal)) {
+ if (DBG) Log.d(TAG, "new proposal accepted");
+ bestIfaceCreationProposal = currentProposal;
+ }
+ }
+ }
+ }
+ }
+
+ if (bestIfaceCreationProposal != null) {
+ IWifiIface iface = executeChipReconfiguration(bestIfaceCreationProposal, ifaceType);
+ if (iface != null) {
+ InterfaceCacheEntry cacheEntry = new InterfaceCacheEntry();
+
+ cacheEntry.chip = bestIfaceCreationProposal.chipInfo.chip;
+ cacheEntry.chipId = bestIfaceCreationProposal.chipInfo.chipId;
+ cacheEntry.name = getName(iface);
+ cacheEntry.type = ifaceType;
+ if (destroyedListener != null) {
+ cacheEntry.destroyedListeners.add(
+ new InterfaceDestroyedListenerProxy(destroyedListener,
+ looper == null ? Looper.myLooper() : looper));
+ }
+
+ mInterfaceInfoCache.put(iface, cacheEntry);
+ return iface;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ // similar to createIfaceIfPossible - but simpler code: not looking for best option just
+ // for any option (so terminates on first one).
+ private boolean isItPossibleToCreateIface(WifiChipInfo[] chipInfos, int ifaceType) {
+ if (DBG) {
+ Log.d(TAG, "isItPossibleToCreateIface: chipInfos=" + Arrays.deepToString(chipInfos)
+ + ", ifaceType=" + ifaceType);
+ }
+
+ for (WifiChipInfo chipInfo: chipInfos) {
+ for (IWifiChip.ChipMode chipMode: chipInfo.availableModes) {
+ for (IWifiChip.ChipIfaceCombination chipIfaceCombo : chipMode
+ .availableCombinations) {
+ int[][] expandedIfaceCombos = expandIfaceCombos(chipIfaceCombo);
+ if (DBG) {
+ Log.d(TAG, chipIfaceCombo + " expands to "
+ + Arrays.deepToString(expandedIfaceCombos));
+ }
+
+ for (int[] expandedIfaceCombo: expandedIfaceCombos) {
+ if (canIfaceComboSupportRequest(chipInfo, chipMode, expandedIfaceCombo,
+ ifaceType) != null) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Expands (or provides an alternative representation) of the ChipIfaceCombination as all
+ * possible combinations of interface.
+ *
+ * Returns [# of combinations][4 (IfaceType)]
+ *
+ * Note: there could be duplicates - allow (inefficient but ...).
+ * TODO: optimize by using a Set as opposed to a []: will remove duplicates. Will need to
+ * provide correct hashes.
+ */
+ private int[][] expandIfaceCombos(IWifiChip.ChipIfaceCombination chipIfaceCombo) {
+ int numOfCombos = 1;
+ for (IWifiChip.ChipIfaceCombinationLimit limit: chipIfaceCombo.limits) {
+ for (int i = 0; i < limit.maxIfaces; ++i) {
+ numOfCombos *= limit.types.size();
+ }
+ }
+
+ int[][] expandedIfaceCombos = new int[numOfCombos][IFACE_TYPES_BY_PRIORITY.length];
+
+ int span = numOfCombos; // span of an individual type (or sub-tree size)
+ for (IWifiChip.ChipIfaceCombinationLimit limit: chipIfaceCombo.limits) {
+ for (int i = 0; i < limit.maxIfaces; ++i) {
+ span /= limit.types.size();
+ for (int k = 0; k < numOfCombos; ++k) {
+ expandedIfaceCombos[k][limit.types.get((k / span) % limit.types.size())]++;
+ }
+ }
+ }
+
+ return expandedIfaceCombos;
+ }
+
+ private class IfaceCreationData {
+ public WifiChipInfo chipInfo;
+ public int chipModeId;
+ public List<WifiIfaceInfo> interfacesToBeRemovedFirst;
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{chipInfo=").append(chipInfo).append(", chipModeId=").append(chipModeId)
+ .append(", interfacesToBeRemovedFirst=").append(interfacesToBeRemovedFirst)
+ .append(")");
+ return sb.toString();
+ }
+ }
+
+ /**
+ * Checks whether the input chip-iface-combo can support the requested interface type: if not
+ * then returns null, if yes then returns information containing the list of interfaces which
+ * would have to be removed first before the requested interface can be created.
+ *
+ * Note: the list of interfaces to be removed is EMPTY if a chip mode change is required - in
+ * that case ALL the interfaces on the current chip have to be removed first.
+ *
+ * Response determined based on:
+ * - Mode configuration: i.e. could the mode support the interface type in principle
+ * - Priority information: i.e. are we 'allowed' to remove interfaces in order to create the
+ * requested interface
+ */
+ private IfaceCreationData canIfaceComboSupportRequest(WifiChipInfo chipInfo,
+ IWifiChip.ChipMode chipMode, int[] chipIfaceCombo, int ifaceType) {
+ if (DBG) {
+ Log.d(TAG, "canIfaceComboSupportRequest: chipInfo=" + chipInfo + ", chipMode="
+ + chipMode + ", chipIfaceCombo=" + chipIfaceCombo + ", ifaceType=" + ifaceType);
+ }
+
+ // short-circuit: does the chipIfaceCombo even support the requested type?
+ if (chipIfaceCombo[ifaceType] == 0) {
+ if (DBG) Log.d(TAG, "Requested type not supported by combo");
+ return null;
+ }
+
+ boolean isChipModeChangeProposed =
+ chipInfo.currentModeIdValid && chipInfo.currentModeId != chipMode.id;
+
+ // short-circuit: can't change chip-mode if an existing interface on this chip has a higher
+ // priority than the requested interface
+ if (isChipModeChangeProposed) {
+ for (int type: IFACE_TYPES_BY_PRIORITY) {
+ if (chipInfo.ifaces[type].length != 0) {
+ if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType)) {
+ if (DBG) {
+ Log.d(TAG, "Couldn't delete existing type " + type
+ + " interfaces for requested type");
+ }
+ return null;
+ }
+ }
+ }
+
+ // but if priority allows the mode change then we're good to go
+ IfaceCreationData ifaceCreationData = new IfaceCreationData();
+ ifaceCreationData.chipInfo = chipInfo;
+ ifaceCreationData.chipModeId = chipMode.id;
+
+ return ifaceCreationData;
+ }
+
+ // possibly supported
+ List<WifiIfaceInfo> interfacesToBeRemovedFirst = new ArrayList<>();
+
+ for (int type: IFACE_TYPES_BY_PRIORITY) {
+ int tooManyInterfaces = chipInfo.ifaces[type].length - chipIfaceCombo[type];
+
+ // need to count the requested interface as well
+ if (type == ifaceType) {
+ tooManyInterfaces += 1;
+ }
+
+ if (tooManyInterfaces > 0) { // may need to delete some
+ if (!allowedToDeleteIfaceTypeForRequestedType(type, ifaceType)) {
+ if (DBG) {
+ Log.d(TAG, "Would need to delete some higher priority interfaces");
+ }
+ return null;
+ }
+
+ // arbitrarily pick the first interfaces to delete
+ for (int i = 0; i < tooManyInterfaces; ++i) {
+ interfacesToBeRemovedFirst.add(chipInfo.ifaces[type][i]);
+ }
+ }
+ }
+
+ IfaceCreationData ifaceCreationData = new IfaceCreationData();
+ ifaceCreationData.chipInfo = chipInfo;
+ ifaceCreationData.chipModeId = chipMode.id;
+ ifaceCreationData.interfacesToBeRemovedFirst = interfacesToBeRemovedFirst;
+
+ return ifaceCreationData;
+ }
+
+ /**
+ * Compares two options to create an interface and determines which is the 'best'. Returns
+ * true if proposal 1 (val1) is better, other false.
+ *
+ * Note: both proposals are 'acceptable' bases on priority criteria.
+ *
+ * Criteria:
+ * - Proposal is better if it means removing fewer high priority interfaces
+ */
+ private boolean compareIfaceCreationData(IfaceCreationData val1, IfaceCreationData val2) {
+ if (DBG) Log.d(TAG, "compareIfaceCreationData: val1=" + val1 + ", val2=" + val2);
+
+ // deal with trivial case of one or the other being null
+ if (val1 == null) {
+ return false;
+ } else if (val2 == null) {
+ return true;
+ }
+
+ for (int type: IFACE_TYPES_BY_PRIORITY) {
+ // # of interfaces to be deleted: the list or all interfaces of the type if mode change
+ int numIfacesToDelete1 = 0;
+ if (val1.chipInfo.currentModeIdValid
+ && val1.chipInfo.currentModeId != val1.chipModeId) {
+ numIfacesToDelete1 = val1.chipInfo.ifaces[type].length;
+ } else {
+ numIfacesToDelete1 = val1.interfacesToBeRemovedFirst.size();
+ }
+
+ int numIfacesToDelete2 = 0;
+ if (val2.chipInfo.currentModeIdValid
+ && val2.chipInfo.currentModeId != val2.chipModeId) {
+ numIfacesToDelete2 = val2.chipInfo.ifaces[type].length;
+ } else {
+ numIfacesToDelete2 = val2.interfacesToBeRemovedFirst.size();
+ }
+
+ if (numIfacesToDelete1 < numIfacesToDelete2) {
+ if (DBG) {
+ Log.d(TAG, "decision based on type=" + type + ": " + numIfacesToDelete1
+ + " < " + numIfacesToDelete2);
+ }
+ return true;
+ }
+ }
+
+ // arbitrary - flip a coin
+ if (DBG) Log.d(TAG, "proposals identical - flip a coin");
+ return false;
+ }
+
+ /**
+ * Returns true if we're allowed to delete the existing interface type for the requested
+ * interface type.
+ *
+ * Rules:
+ * 1. Request for AP or STA will destroy any other interface (except see #4)
+ * 2. Request for P2P will destroy NAN-only
+ * 3. Request for NAN will not destroy any interface
+ * --
+ * 4. No interface will be destroyed for a requested interface of the same type
+ */
+ private boolean allowedToDeleteIfaceTypeForRequestedType(int existingIfaceType,
+ int requestedIfaceType) {
+ // rule 4
+ if (existingIfaceType == requestedIfaceType) {
+ return false;
+ }
+
+ // rule 3
+ if (requestedIfaceType == IfaceType.NAN) {
+ return false;
+ }
+
+ // rule 2
+ if (requestedIfaceType == IfaceType.P2P) {
+ return existingIfaceType == IfaceType.NAN;
+ }
+
+ // rule 1, the requestIfaceType is either AP or STA
+ return true;
+ }
+
+ /**
+ * Performs chip reconfiguration per the input:
+ * - Removes the specified interfaces
+ * - Reconfigures the chip to the new chip mode (if necessary)
+ * - Creates the new interface
+ *
+ * Returns the newly created interface or a null on any error.
+ */
+ private IWifiIface executeChipReconfiguration(IfaceCreationData ifaceCreationData,
+ int ifaceType) {
+ if (DBG) {
+ Log.d(TAG, "executeChipReconfiguration: ifaceCreationData=" + ifaceCreationData
+ + ", ifaceType=" + ifaceType);
+ }
+ synchronized (mLock) {
+ try {
+ // is this a mode change?
+ boolean isModeConfigNeeded = !ifaceCreationData.chipInfo.currentModeIdValid
+ || ifaceCreationData.chipInfo.currentModeId != ifaceCreationData.chipModeId;
+ if (DBG) Log.d(TAG, "isModeConfigNeeded=" + isModeConfigNeeded);
+
+ // first delete interfaces/change modes
+ if (isModeConfigNeeded) {
+ // remove all interfaces pre mode-change
+ // TODO: is this necessary? note that even if we don't want to explicitly
+ // remove the interfaces we do need to call the onDeleted callbacks - which
+ // this does
+ for (WifiIfaceInfo[] ifaceInfos: ifaceCreationData.chipInfo.ifaces) {
+ for (WifiIfaceInfo ifaceInfo: ifaceInfos) {
+ removeIfaceInternal(ifaceInfo.iface); // ignore return value
+ }
+ }
+
+ WifiStatus status = ifaceCreationData.chipInfo.chip.configureChip(
+ ifaceCreationData.chipModeId);
+ if (status.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG, "executeChipReconfiguration: configureChip error: "
+ + statusString(status));
+ return null;
+ }
+ } else {
+ // remove all interfaces on the delete list
+ for (WifiIfaceInfo ifaceInfo: ifaceCreationData.interfacesToBeRemovedFirst) {
+ removeIfaceInternal(ifaceInfo.iface); // ignore return value
+ }
+ }
+
+ // create new interface
+ Mutable<WifiStatus> statusResp = new Mutable<>();
+ Mutable<IWifiIface> ifaceResp = new Mutable<>();
+ switch (ifaceType) {
+ case IfaceType.STA:
+ ifaceCreationData.chipInfo.chip.createStaIface(
+ (WifiStatus status, IWifiStaIface iface) -> {
+ statusResp.value = status;
+ ifaceResp.value = iface;
+ });
+ break;
+ case IfaceType.AP:
+ ifaceCreationData.chipInfo.chip.createApIface(
+ (WifiStatus status, IWifiApIface iface) -> {
+ statusResp.value = status;
+ ifaceResp.value = iface;
+ });
+ break;
+ case IfaceType.P2P:
+ ifaceCreationData.chipInfo.chip.createP2pIface(
+ (WifiStatus status, IWifiP2pIface iface) -> {
+ statusResp.value = status;
+ ifaceResp.value = iface;
+ });
+ break;
+ case IfaceType.NAN:
+ ifaceCreationData.chipInfo.chip.createNanIface(
+ (WifiStatus status, IWifiNanIface iface) -> {
+ statusResp.value = status;
+ ifaceResp.value = iface;
+ });
+ break;
+ }
+
+ if (statusResp.value.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG, "executeChipReconfiguration: failed to create interface ifaceType="
+ + ifaceType + ": " + statusString(statusResp.value));
+ return null;
+ }
+
+ return ifaceResp.value;
+ } catch (RemoteException e) {
+ Log.e(TAG, "executeChipReconfiguration exception: " + e);
+ return null;
+ }
+ }
+ }
+
+ private boolean removeIfaceInternal(IWifiIface iface) {
+ if (DBG) Log.d(TAG, "removeIfaceInternal: iface(name)=" + getName(iface));
+
+ synchronized (mLock) {
+ if (mWifi == null) {
+ Log.e(TAG, "removeIfaceInternal: null IWifi -- iface(name)=" + getName(iface));
+ return false;
+ }
+
+ IWifiChip chip = getChip(iface);
+ if (chip == null) {
+ Log.e(TAG, "removeIfaceInternal: null IWifiChip -- iface(name)=" + getName(iface));
+ return false;
+ }
+
+ String name = getName(iface);
+ if (name == null) {
+ Log.e(TAG, "removeIfaceInternal: can't get name");
+ return false;
+ }
+
+ int type = getType(iface);
+ if (type == -1) {
+ Log.e(TAG, "removeIfaceInternal: can't get type -- iface(name)=" + getName(iface));
+ return false;
+ }
+
+ WifiStatus status = null;
+ try {
+ switch (type) {
+ case IfaceType.STA:
+ status = chip.removeStaIface(name);
+ break;
+ case IfaceType.AP:
+ status = chip.removeApIface(name);
+ break;
+ case IfaceType.P2P:
+ status = chip.removeP2pIface(name);
+ break;
+ case IfaceType.NAN:
+ status = chip.removeNanIface(name);
+ break;
+ default:
+ Log.wtf(TAG, "removeIfaceInternal: invalid type=" + type);
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "IWifiChip.removeXxxIface exception: " + e);
+ }
+
+ // dispatch listeners no matter what status
+ dispatchDestroyedListeners(iface);
+
+ if (status != null && status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "IWifiChip.removeXxxIface failed: " + statusString(status));
+ return false;
+ }
+ }
+ }
+
+ // dispatch all available for request listeners of the specified type AND clean-out the list:
+ // listeners are called once at most!
+ private boolean dispatchAvailableForRequestListeners() {
+ if (DBG) Log.d(TAG, "dispatchAvailableForRequestListeners");
+
+ synchronized (mLock) {
+ WifiChipInfo[] chipInfos = getAllChipInfo();
+ if (chipInfos == null) {
+ Log.e(TAG, "dispatchAvailableForRequestListeners: no chip info found");
+ stopWifi(); // major error: shutting down
+ return false;
+ }
+ if (DBG) {
+ Log.d(TAG, "dispatchAvailableForRequestListeners: chipInfos="
+ + Arrays.deepToString(chipInfos));
+ }
+
+ for (int ifaceType : IFACE_TYPES_BY_PRIORITY) {
+ dispatchAvailableForRequestListenersForType(ifaceType, chipInfos);
+ }
+ }
+
+ return true;
+ }
+
+ private void dispatchAvailableForRequestListenersForType(int ifaceType,
+ WifiChipInfo[] chipInfos) {
+ if (DBG) Log.d(TAG, "dispatchAvailableForRequestListenersForType: ifaceType=" + ifaceType);
+
+ Set<InterfaceAvailableForRequestListenerProxy> listeners =
+ mInterfaceAvailableForRequestListeners.get(ifaceType);
+
+ if (listeners.size() == 0) {
+ return;
+ }
+
+ if (!isItPossibleToCreateIface(chipInfos, ifaceType)) {
+ if (DBG) Log.d(TAG, "Creating interface type isn't possible: ifaceType=" + ifaceType);
+ return;
+ }
+
+ if (DBG) Log.d(TAG, "It is possible to create the interface type: ifaceType=" + ifaceType);
+ for (InterfaceAvailableForRequestListenerProxy listener : listeners) {
+ listener.trigger();
+ }
+ }
+
+ // dispatch all destroyed listeners registered for the specified interface AND remove the
+ // cache entry
+ private void dispatchDestroyedListeners(IWifiIface iface) {
+ if (DBG) Log.d(TAG, "dispatchDestroyedListeners: iface(name)=" + getName(iface));
+
+ synchronized (mLock) {
+ InterfaceCacheEntry entry = mInterfaceInfoCache.get(iface);
+ if (entry == null) {
+ Log.e(TAG, "dispatchDestroyedListeners: no cache entry for iface(name)="
+ + getName(iface));
+ return;
+ }
+
+ for (InterfaceDestroyedListenerProxy listener : entry.destroyedListeners) {
+ listener.trigger();
+ }
+ entry.destroyedListeners.clear(); // for insurance (though cache entry is removed)
+ mInterfaceInfoCache.remove(iface);
+ }
+ }
+
+ // dispatch all destroyed listeners registered to all interfaces
+ private void dispatchAllDestroyedListeners() {
+ if (DBG) Log.d(TAG, "dispatchAllDestroyedListeners");
+
+ synchronized (mLock) {
+ Iterator<Map.Entry<IWifiIface, InterfaceCacheEntry>> it =
+ mInterfaceInfoCache.entrySet().iterator();
+ while (it.hasNext()) {
+ InterfaceCacheEntry entry = it.next().getValue();
+ for (InterfaceDestroyedListenerProxy listener : entry.destroyedListeners) {
+ listener.trigger();
+ }
+ entry.destroyedListeners.clear(); // for insurance (though cache entry is removed)
+ it.remove();
+ }
+ }
+ }
+
+ private abstract class ListenerProxy<LISTENER> {
+ private static final int LISTENER_TRIGGERED = 0;
+
+ protected LISTENER mListener;
+ private Handler mHandler;
+
+ // override equals & hash to make sure that the container HashSet is unique with respect to
+ // the contained listener
+ @Override
+ public boolean equals(Object obj) {
+ return mListener == ((ListenerProxy<LISTENER>) obj).mListener;
+ }
+
+ @Override
+ public int hashCode() {
+ return mListener.hashCode();
+ }
+
+ void trigger() {
+ mHandler.sendMessage(mHandler.obtainMessage(LISTENER_TRIGGERED));
+ }
+
+ protected abstract void action();
+
+ ListenerProxy(LISTENER listener, Looper looper, String tag) {
+ mListener = listener;
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (DBG) {
+ Log.d(tag, "ListenerProxy.handleMessage: what=" + msg.what);
+ }
+ switch (msg.what) {
+ case LISTENER_TRIGGERED:
+ action();
+ break;
+ default:
+ Log.e(tag, "ListenerProxy.handleMessage: unknown message what="
+ + msg.what);
+ }
+ }
+ };
+ }
+ }
+
+ private class InterfaceDestroyedListenerProxy extends
+ ListenerProxy<InterfaceDestroyedListener> {
+ InterfaceDestroyedListenerProxy(InterfaceDestroyedListener destroyedListener,
+ Looper looper) {
+ super(destroyedListener, looper, "InterfaceDestroyedListenerProxy");
+ }
+
+ @Override
+ protected void action() {
+ mListener.onDestroyed();
+ }
+ }
+
+ private class InterfaceAvailableForRequestListenerProxy extends
+ ListenerProxy<InterfaceAvailableForRequestListener> {
+ InterfaceAvailableForRequestListenerProxy(
+ InterfaceAvailableForRequestListener destroyedListener, Looper looper) {
+ super(destroyedListener, looper, "InterfaceAvailableForRequestListenerProxy");
+ }
+
+ @Override
+ protected void action() {
+ mListener.onAvailableForRequest();
+ }
+ }
+
+ // general utilities
+
+ private static String statusString(WifiStatus status) {
+ if (status == null) {
+ return "status=null";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(status.code).append(" (").append(status.description).append(")");
+ return sb.toString();
+ }
+
+ // Will return -1 for invalid results! Otherwise will return one of the 4 valid values.
+ private static int getType(IWifiIface iface) {
+ MutableInt typeResp = new MutableInt(-1);
+ try {
+ iface.getType((WifiStatus status, int type) -> {
+ if (status.code == WifiStatusCode.SUCCESS) {
+ typeResp.value = type;
+ } else {
+ Log.e(TAG, "Error on getType: " + statusString(status));
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception on getType: " + e);
+ }
+
+ return typeResp.value;
+ }
+
+ private static class Mutable<E> {
+ public E value;
+
+ Mutable() {
+ value = null;
+ }
+
+ Mutable(E value) {
+ this.value = value;
+ }
+ }
+
+ /**
+ * Dump the internal state of the class.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("HalDeviceManager:");
+ pw.println(" mServiceManager: " + mServiceManager);
+ pw.println(" mWifi: " + mWifi);
+ pw.println(" mManagerStatusListeners: " + mManagerStatusListeners);
+ pw.println(" mInterfaceAvailableForRequestListeners: "
+ + mInterfaceAvailableForRequestListeners);
+ pw.println(" mInterfaceInfoCache: " + mInterfaceInfoCache);
+ }
+}
diff --git a/service/java/com/android/server/wifi/IMSIParameter.java b/service/java/com/android/server/wifi/IMSIParameter.java
index deea870..ab9ec0a 100644
--- a/service/java/com/android/server/wifi/IMSIParameter.java
+++ b/service/java/com/android/server/wifi/IMSIParameter.java
@@ -1,8 +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.
+ */
+
package com.android.server.wifi;
-import java.io.IOException;
+import android.text.TextUtils;
+/**
+ * Class for storing an IMSI (International Mobile Subscriber Identity) parameter. The IMSI
+ * contains number (up to 15) of numerical digits. When an IMSI ends with a '*', the specified
+ * IMSI is a prefix.
+ */
public class IMSIParameter {
+ private static final int MAX_IMSI_LENGTH = 15;
+
+ /**
+ * MCC (Mobile Country Code) is a 3 digit number and MNC (Mobile Network Code) is also a 3
+ * digit number.
+ */
+ private static final int MCC_MNC_LENGTH = 6;
+
private final String mImsi;
private final boolean mPrefix;
@@ -11,11 +40,22 @@
mPrefix = prefix;
}
- public IMSIParameter(String imsi) throws IOException {
- if (imsi == null || imsi.length() == 0) {
- throw new IOException("Bad IMSI: '" + imsi + "'");
+ /**
+ * Build an IMSIParameter object from the given string. A null will be returned for a
+ * malformed string.
+ *
+ * @param imsi The IMSI string
+ * @return {@link IMSIParameter}
+ */
+ public static IMSIParameter build(String imsi) {
+ if (TextUtils.isEmpty(imsi)) {
+ return null;
+ }
+ if (imsi.length() > MAX_IMSI_LENGTH) {
+ return null;
}
+ // Detect the first non-digit character.
int nonDigit;
char stopChar = '\0';
for (nonDigit = 0; nonDigit < imsi.length(); nonDigit++) {
@@ -26,48 +66,55 @@
}
if (nonDigit == imsi.length()) {
- mImsi = imsi;
- mPrefix = false;
+ // Full IMSI.
+ return new IMSIParameter(imsi, false);
}
else if (nonDigit == imsi.length()-1 && stopChar == '*') {
- mImsi = imsi.substring(0, nonDigit);
- mPrefix = true;
+ // IMSI prefix.
+ return new IMSIParameter(imsi.substring(0, nonDigit), true);
}
- else {
- throw new IOException("Bad IMSI: '" + imsi + "'");
- }
+ return null;
}
- public boolean matches(String fullIMSI) {
- if (mPrefix) {
- return mImsi.regionMatches(false, 0, fullIMSI, 0, mImsi.length());
+ /**
+ * Perform matching against the given full IMSI.
+ *
+ * @param fullIMSI The full IMSI to match against
+ * @return true if matched
+ */
+ public boolean matchesImsi(String fullIMSI) {
+ if (fullIMSI == null) {
+ return false;
}
- else {
+
+ if (mPrefix) {
+ // Prefix matching.
+ return mImsi.regionMatches(false, 0, fullIMSI, 0, mImsi.length());
+ } else {
+ // Exact matching.
return mImsi.equals(fullIMSI);
}
}
+ /**
+ * Perform matching against the given MCC-MNC (Mobile Country Code and Mobile Network
+ * Code) combination.
+ *
+ * @param mccMnc The MCC-MNC to match against
+ * @return true if matched
+ */
public boolean matchesMccMnc(String mccMnc) {
- if (mPrefix) {
- // For a prefix match, the entire prefix must match the mcc+mnc
- return mImsi.regionMatches(false, 0, mccMnc, 0, mImsi.length());
+ if (mccMnc == null) {
+ return false;
}
- else {
- // For regular match, the entire length of mcc+mnc must match this IMSI
- return mImsi.regionMatches(false, 0, mccMnc, 0, mccMnc.length());
+ if (mccMnc.length() != MCC_MNC_LENGTH) {
+ return false;
}
- }
-
- public boolean isPrefix() {
- return mPrefix;
- }
-
- public String getImsi() {
- return mImsi;
- }
-
- public int prefixLength() {
- return mImsi.length();
+ int checkLength = MCC_MNC_LENGTH;
+ if (mPrefix && mImsi.length() < MCC_MNC_LENGTH) {
+ checkLength = mImsi.length();
+ }
+ return mImsi.regionMatches(false, 0, mccMnc, 0, checkLength);
}
@Override
@@ -75,12 +122,12 @@
if (this == thatObject) {
return true;
}
- else if (thatObject == null || getClass() != thatObject.getClass()) {
+ if (!(thatObject instanceof IMSIParameter)) {
return false;
}
IMSIParameter that = (IMSIParameter) thatObject;
- return mPrefix == that.mPrefix && mImsi.equals(that.mImsi);
+ return mPrefix == that.mPrefix && TextUtils.equals(mImsi, that.mImsi);
}
@Override
diff --git a/service/java/com/android/server/wifi/LastMileLogger.java b/service/java/com/android/server/wifi/LastMileLogger.java
new file mode 100644
index 0000000..0fb810c
--- /dev/null
+++ b/service/java/com/android/server/wifi/LastMileLogger.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.os.FileUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.io.IoUtils;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * Provides a facility for capturing kernel trace events related to Wifi control and data paths.
+ */
+public class LastMileLogger {
+ public LastMileLogger(WifiInjector injector) {
+ this(injector, WIFI_EVENT_BUFFER_PATH, WIFI_EVENT_ENABLE_PATH, WIFI_EVENT_RELEASE_PATH);
+ }
+
+ @VisibleForTesting
+ public LastMileLogger(WifiInjector injector, String bufferPath, String enablePath,
+ String releasePath) {
+ mLog = injector.makeLog(TAG);
+ mEventBufferPath = bufferPath;
+ mEventEnablePath = enablePath;
+ mEventReleasePath = releasePath;
+ }
+
+ /**
+ * Informs LastMileLogger that a connection event has occurred.
+ * @param connectionId A non-negative connection identifier, or -1 to indicate unknown
+ * @param event an event defined in BaseWifiDiagnostics
+ */
+ public void reportConnectionEvent(long connectionId, byte event) {
+ if (connectionId < 0) {
+ mLog.warn("Ignoring negative connection id: %").c(connectionId);
+ return;
+ }
+
+ switch (event) {
+ case BaseWifiDiagnostics.CONNECTION_EVENT_STARTED:
+ mPendingConnectionId = connectionId;
+ enableTracing();
+ return;
+ case BaseWifiDiagnostics.CONNECTION_EVENT_SUCCEEDED:
+ mPendingConnectionId = -1;
+ disableTracing();
+ return;
+ case BaseWifiDiagnostics.CONNECTION_EVENT_FAILED:
+ if (connectionId >= mPendingConnectionId) {
+ mPendingConnectionId = -1;
+ disableTracing();
+ mLastMileLogForLastFailure = readTrace();
+ return;
+ } else {
+ // Spurious failure message. Here's one scenario where this might happen:
+ // t=00sec start first connection attempt
+ // t=30sec start second connection attempt
+ // t=60sec timeout first connection attempt
+ // We should not stop tracing in this case, since the second connection attempt
+ // is still in progress.
+ return;
+ }
+ }
+ }
+
+ /**
+ * Dumps the contents of the log.
+ * @param pw the PrintWriter that will receive the dump
+ */
+ public void dump(PrintWriter pw) {
+ dumpInternal(pw, "Last failed last-mile log", mLastMileLogForLastFailure);
+ dumpInternal(pw, "Latest last-mile log", readTrace());
+ mLastMileLogForLastFailure = null;
+ }
+
+ private static final String TAG = "LastMileLogger";
+ private static final String WIFI_EVENT_BUFFER_PATH =
+ "/sys/kernel/debug/tracing/instances/wifi/trace";
+ private static final String WIFI_EVENT_ENABLE_PATH =
+ "/sys/kernel/debug/tracing/instances/wifi/tracing_on";
+ private static final String WIFI_EVENT_RELEASE_PATH =
+ "/sys/kernel/debug/tracing/instances/wifi/free_buffer";
+
+ private final String mEventBufferPath;
+ private final String mEventEnablePath;
+ private final String mEventReleasePath;
+ private WifiLog mLog;
+ private byte[] mLastMileLogForLastFailure;
+ private FileInputStream mLastMileTraceHandle;
+ private long mPendingConnectionId = -1;
+
+ private void enableTracing() {
+ if (!ensureFailSafeIsArmed()) {
+ mLog.wC("Failed to arm fail-safe.");
+ return;
+ }
+
+ try {
+ FileUtils.stringToFile(mEventEnablePath, "1");
+ } catch (IOException e) {
+ mLog.warn("Failed to start event tracing: %").r(e.getMessage()).flush();
+ }
+ }
+
+ private void disableTracing() {
+ try {
+ FileUtils.stringToFile(mEventEnablePath, "0");
+ } catch (IOException e) {
+ mLog.warn("Failed to stop event tracing: %").r(e.getMessage()).flush();
+ }
+ }
+
+ private byte[] readTrace() {
+ try {
+ return IoUtils.readFileAsByteArray(mEventBufferPath);
+ } catch (IOException e) {
+ mLog.warn("Failed to read event trace: %").r(e.getMessage()).flush();
+ return new byte[0];
+ }
+ }
+
+ private boolean ensureFailSafeIsArmed() {
+ if (mLastMileTraceHandle != null) {
+ return true;
+ }
+
+ try {
+ // This file provides fail-safe behavior for Last-Mile logging. Given that we:
+ // 1. Set the disable_on_free option in the trace_options pseudo-file
+ // (see wifi-events.rc), and
+ // 2. Hold the WIFI_EVENT_RELEASE_PATH open,
+ //
+ // Then, when this process dies, the kernel will automatically disable any
+ // tracing in the wifi trace instance.
+ //
+ // Note that, despite Studio's suggestion that |mLastMileTraceHandle| could be demoted
+ // to a local variable, we need to stick with a field. Otherwise, the handle could be
+ // garbage collected.
+ mLastMileTraceHandle = new FileInputStream(mEventReleasePath);
+ return true;
+ } catch (IOException e) {
+ mLog.warn("Failed to open free_buffer pseudo-file: %").r(e.getMessage()).flush();
+ return false;
+ }
+ }
+
+ private static void dumpInternal(PrintWriter pw, String description, byte[] lastMileLog) {
+ if (lastMileLog == null || lastMileLog.length < 1) {
+ pw.format("No last mile log for \"%s\"\n", description);
+ return;
+ }
+
+ pw.format("-------------------------- %s ---------------------------\n", description);
+ pw.print(new String(lastMileLog));
+ pw.println("--------------------------------------------------------------------");
+ }
+}
diff --git a/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java b/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java
new file mode 100644
index 0000000..9e3b4fb
--- /dev/null
+++ b/service/java/com/android/server/wifi/LocalOnlyHotspotRequestInfo.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.annotation.NonNull;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Tracks information about applications requesting use of the LocalOnlyHotspot.
+ *
+ * @hide
+ */
+public class LocalOnlyHotspotRequestInfo implements IBinder.DeathRecipient {
+ static final int HOTSPOT_NO_ERROR = -1;
+
+ private final int mPid;
+ private final IBinder mBinder;
+ private final RequestingApplicationDeathCallback mCallback;
+ private final Messenger mMessenger;
+
+ /**
+ * Callback for use with LocalOnlyHotspot to unregister requesting applications upon death.
+ */
+ public interface RequestingApplicationDeathCallback {
+ /**
+ * Called when requesting app has died.
+ */
+ void onLocalOnlyHotspotRequestorDeath(LocalOnlyHotspotRequestInfo requestor);
+ }
+
+ LocalOnlyHotspotRequestInfo(@NonNull IBinder binder, @NonNull Messenger messenger,
+ @NonNull RequestingApplicationDeathCallback callback) {
+ mPid = Binder.getCallingPid();
+ mBinder = Preconditions.checkNotNull(binder);
+ mMessenger = Preconditions.checkNotNull(messenger);
+ mCallback = Preconditions.checkNotNull(callback);
+
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ binderDied();
+ }
+ }
+
+ /**
+ * Allow caller to unlink this object from binder death.
+ */
+ public void unlinkDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
+ }
+
+ /**
+ * Application requesting LocalOnlyHotspot died
+ */
+ @Override
+ public void binderDied() {
+ mCallback.onLocalOnlyHotspotRequestorDeath(this);
+ }
+
+ /**
+ * Send a HOTSPOT_FAILED message to WifiManager for the calling application with the error code.
+ *
+ * @param reasonCode error code for the message
+ *
+ * @throws RemoteException
+ */
+ public void sendHotspotFailedMessage(int reasonCode) throws RemoteException {
+ Message message = Message.obtain();
+ message.what = WifiManager.HOTSPOT_FAILED;
+ message.arg1 = reasonCode;
+ mMessenger.send(message);
+ }
+
+ /**
+ * Send a HOTSPOT_STARTED message to WifiManager for the calling application with the config.
+ *
+ * @param config WifiConfiguration for the callback
+ *
+ * @throws RemoteException
+ */
+ public void sendHotspotStartedMessage(WifiConfiguration config) throws RemoteException {
+ Message message = Message.obtain();
+ message.what = WifiManager.HOTSPOT_STARTED;
+ message.obj = config;
+ mMessenger.send(message);
+ }
+
+ /**
+ * Send a HOTSPOT_STOPPED message to WifiManager for the calling application.
+ *
+ * @throws RemoteException
+ */
+ public void sendHotspotStoppedMessage() throws RemoteException {
+ Message message = Message.obtain();
+ message.what = WifiManager.HOTSPOT_STOPPED;
+ mMessenger.send(message);
+ }
+
+ public int getPid() {
+ return mPid;
+ }
+}
diff --git a/service/java/com/android/server/wifi/LogcatLog.java b/service/java/com/android/server/wifi/LogcatLog.java
new file mode 100644
index 0000000..ffe8950
--- /dev/null
+++ b/service/java/com/android/server/wifi/LogcatLog.java
@@ -0,0 +1,219 @@
+/*
+ * 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.util.Log;
+
+import com.android.internal.annotations.Immutable;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Provides a WifiLog implementation which uses logd as the
+ * logging backend.
+ *
+ * This class is trivially thread-safe, as instances are immutable.
+ * Note, however, that LogMessage instances are _not_ thread-safe.
+ */
+@ThreadSafe
+@Immutable
+class LogcatLog implements WifiLog {
+ private final String mTag;
+ private static volatile boolean sVerboseLogging = false;
+
+ LogcatLog(String tag) {
+ mTag = tag;
+ }
+
+ public static void enableVerboseLogging(int verboseMode) {
+ if (verboseMode > 0) {
+ sVerboseLogging = true;
+ } else {
+ sVerboseLogging = false;
+ }
+ }
+
+ /* New-style methods */
+ @Override
+ public LogMessage err(String format) {
+ return makeLogMessage(Log.ERROR, format);
+ }
+
+ @Override
+ public LogMessage warn(String format) {
+ return makeLogMessage(Log.WARN, format);
+ }
+
+ @Override
+ public LogMessage info(String format) {
+ return makeLogMessage(Log.INFO, format);
+ }
+
+ @Override
+ public LogMessage trace(String format) {
+ return makeLogMessage(Log.DEBUG, format);
+ }
+
+ @Override
+ public LogMessage dump(String format) {
+ return makeLogMessage(Log.VERBOSE, format);
+ }
+
+ @Override
+ public void eC(String msg) {
+ Log.e(mTag, msg);
+ }
+
+ @Override
+ public void wC(String msg) {
+ Log.w(mTag, msg);
+ }
+
+ @Override
+ public void iC(String msg) {
+ Log.i(mTag, msg);
+ }
+
+ @Override
+ public void tC(String msg) {
+ Log.d(mTag, msg);
+ }
+
+ /* Legacy methods */
+ @Override
+ public void e(String msg) {
+ Log.e(mTag, msg);
+ }
+
+ @Override
+ public void w(String msg) {
+ Log.w(mTag, msg);
+ }
+
+ @Override
+ public void i(String msg) {
+ Log.i(mTag, msg);
+ }
+
+ @Override
+ public void d(String msg) {
+ Log.d(mTag, msg);
+ }
+
+ @Override
+ public void v(String msg) {
+ Log.v(mTag, msg);
+ }
+
+ /* Internal details */
+ private static class RealLogMessage implements WifiLog.LogMessage {
+ private final int mLogLevel;
+ private final String mTag;
+ private final String mFormat;
+ private final StringBuilder mStringBuilder;
+ private int mNextFormatCharPos;
+
+ RealLogMessage(int logLevel, String tag, String format) {
+ mLogLevel = logLevel;
+ mTag = tag;
+ mFormat = format;
+ mStringBuilder = new StringBuilder();
+ mNextFormatCharPos = 0;
+ }
+
+ @Override
+ public WifiLog.LogMessage r(String value) {
+ // Since the logcat back-end is just transitional, we don't attempt to tag sensitive
+ // information in it.
+ return c(value);
+ }
+
+ @Override
+ public WifiLog.LogMessage c(String value) {
+ copyUntilPlaceholder();
+ if (mNextFormatCharPos < mFormat.length()) {
+ mStringBuilder.append(value);
+ ++mNextFormatCharPos;
+ }
+ return this;
+ }
+
+ @Override
+ public WifiLog.LogMessage c(long value) {
+ copyUntilPlaceholder();
+ if (mNextFormatCharPos < mFormat.length()) {
+ mStringBuilder.append(value);
+ ++mNextFormatCharPos;
+ }
+ return this;
+ }
+
+ @Override
+ public WifiLog.LogMessage c(char value) {
+ copyUntilPlaceholder();
+ if (mNextFormatCharPos < mFormat.length()) {
+ mStringBuilder.append(value);
+ ++mNextFormatCharPos;
+ }
+ return this;
+ }
+
+ @Override
+ public WifiLog.LogMessage c(boolean value) {
+ copyUntilPlaceholder();
+ if (mNextFormatCharPos < mFormat.length()) {
+ mStringBuilder.append(value);
+ ++mNextFormatCharPos;
+ }
+ return this;
+ }
+
+ @Override
+ public void flush() {
+ if (mNextFormatCharPos < mFormat.length()) {
+ mStringBuilder.append(mFormat, mNextFormatCharPos, mFormat.length());
+ }
+ if (sVerboseLogging || mLogLevel > Log.DEBUG) {
+ Log.println(mLogLevel, mTag, mStringBuilder.toString());
+ }
+ }
+
+ /* Should generally not be used; implemented primarily to aid in testing. */
+ public String toString() {
+ return mStringBuilder.toString();
+ }
+
+ private void copyUntilPlaceholder() {
+ if (mNextFormatCharPos >= mFormat.length()) {
+ return;
+ }
+
+ int placeholderPos = mFormat.indexOf(WifiLog.PLACEHOLDER, mNextFormatCharPos);
+ if (placeholderPos == -1) {
+ placeholderPos = mFormat.length();
+ }
+
+ mStringBuilder.append(mFormat, mNextFormatCharPos, placeholderPos);
+ mNextFormatCharPos = placeholderPos;
+ }
+ }
+
+ private LogMessage makeLogMessage(int logLevel, String format) {
+ // TODO(b/30737821): Consider adding an isLoggable() check.
+ return new RealLogMessage(logLevel, mTag, format);
+ }
+}
diff --git a/service/java/com/android/server/wifi/NetworkListStoreData.java b/service/java/com/android/server/wifi/NetworkListStoreData.java
new file mode 100644
index 0000000..5ddfd4d
--- /dev/null
+++ b/service/java/com/android/server/wifi/NetworkListStoreData.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.net.IpConfiguration;
+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.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.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class performs serialization and parsing of XML data block that contain the list of WiFi
+ * network configurations (XML block data inside <NetworkList> tag).
+ */
+public class NetworkListStoreData implements WifiConfigStore.StoreData {
+ private static final String TAG = "NetworkListStoreData";
+
+ 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";
+
+ /**
+ * List of saved shared networks visible to all the users to be stored in the shared store file.
+ */
+ private List<WifiConfiguration> mSharedConfigurations;
+ /**
+ * List of saved private networks only visible to the current user to be stored in the user
+ * specific store file.
+ */
+ private List<WifiConfiguration> mUserConfigurations;
+
+ NetworkListStoreData() {}
+
+ @Override
+ public void serializeData(XmlSerializer out, boolean shared)
+ throws XmlPullParserException, IOException {
+ if (shared) {
+ serializeNetworkList(out, mSharedConfigurations);
+ } else {
+ serializeNetworkList(out, mUserConfigurations);
+ }
+ }
+
+ @Override
+ public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
+ throws XmlPullParserException, IOException {
+ if (shared) {
+ mSharedConfigurations = parseNetworkList(in, outerTagDepth);
+ } else {
+ mUserConfigurations = parseNetworkList(in, outerTagDepth);
+ }
+ }
+
+ @Override
+ public void resetData(boolean shared) {
+ if (shared) {
+ mSharedConfigurations = null;
+ } else {
+ mUserConfigurations = null;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return XML_TAG_SECTION_HEADER_NETWORK_LIST;
+ }
+
+ @Override
+ public boolean supportShareData() {
+ return true;
+ }
+
+ public void setSharedConfigurations(List<WifiConfiguration> configs) {
+ mSharedConfigurations = configs;
+ }
+
+ /**
+ * An empty list will be returned if no shared configurations.
+ *
+ * @return List of {@link WifiConfiguration}
+ */
+ public List<WifiConfiguration> getSharedConfigurations() {
+ if (mSharedConfigurations == null) {
+ return new ArrayList<WifiConfiguration>();
+ }
+ return mSharedConfigurations;
+ }
+
+ public void setUserConfigurations(List<WifiConfiguration> configs) {
+ mUserConfigurations = configs;
+ }
+
+ /**
+ * An empty list will be returned if no user configurations.
+ *
+ * @return List of {@link WifiConfiguration}
+ */
+ public List<WifiConfiguration> getUserConfigurations() {
+ if (mUserConfigurations == null) {
+ return new ArrayList<WifiConfiguration>();
+ }
+ return mUserConfigurations;
+ }
+
+ /**
+ * Serialize the list of {@link WifiConfiguration} to an output stream in XML format.
+ *
+ * @param out The output stream to serialize the data to
+ * @param networkList The network list to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private void serializeNetworkList(XmlSerializer out, List<WifiConfiguration> networkList)
+ throws XmlPullParserException, IOException {
+ if (networkList == null) {
+ return;
+ }
+ for (WifiConfiguration network : networkList) {
+ serializeNetwork(out, network);
+ }
+ }
+
+ /**
+ * Serialize a {@link WifiConfiguration} to an output stream in XML format.
+ * @param out
+ * @param config
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private void serializeNetwork(XmlSerializer out, WifiConfiguration config)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK);
+
+ // Serialize WifiConfiguration.
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
+ WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, config);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
+
+ // Serialize network selection status.
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS);
+ NetworkSelectionStatusXmlUtil.writeToXml(out, config.getNetworkSelectionStatus());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS);
+
+ // Serialize IP configuration.
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
+ IpConfigurationXmlUtil.writeToXml(out, config.getIpConfiguration());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
+
+ // Serialize enterprise configuration for enterprise networks.
+ if (config.enterpriseConfig != null
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
+ XmlUtil.writeNextSectionStart(
+ out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
+ WifiEnterpriseConfigXmlUtil.writeToXml(out, config.enterpriseConfig);
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
+ }
+
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK);
+ }
+
+ /**
+ * Parse a list of {@link WifiConfiguration} from an input stream in XML format.
+ *
+ * @param in The input stream to read from
+ * @param outerTagDepth The XML tag depth of the outer XML block
+ * @return List of {@link WifiConfiguration}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private List<WifiConfiguration> parseNetworkList(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ List<WifiConfiguration> networkList = new ArrayList<>();
+ while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_NETWORK,
+ outerTagDepth)) {
+ // Try/catch only runtime exceptions (like illegal args), any XML/IO exceptions are
+ // fatal and should abort the entire loading process.
+ try {
+ WifiConfiguration config = parseNetwork(in, outerTagDepth + 1);
+ networkList.add(config);
+ } catch (RuntimeException e) {
+ // Failed to parse this network, skip it.
+ Log.e(TAG, "Failed to parse network config. Skipping...", e);
+ }
+ }
+ return networkList;
+ }
+
+ /**
+ * Parse a {@link WifiConfiguration} from an input stream in XML format.
+ *
+ * @param in The input stream to read from
+ * @param outerTagDepth The XML tag depth of the outer XML block
+ * @return {@link WifiConfiguration}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private WifiConfiguration parseNetwork(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ Pair<String, WifiConfiguration> parsedConfig = null;
+ NetworkSelectionStatus status = null;
+ IpConfiguration ipConfiguration = null;
+ WifiEnterpriseConfig enterpriseConfig = null;
+
+ String[] headerName = new String[1];
+ while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) {
+ switch (headerName[0]) {
+ case XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION:
+ if (parsedConfig != null) {
+ throw new XmlPullParserException("Detected duplicate tag for: "
+ + XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION);
+ }
+ parsedConfig = WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1);
+ break;
+ case XML_TAG_SECTION_HEADER_NETWORK_STATUS:
+ if (status != null) {
+ throw new XmlPullParserException("Detected duplicate tag for: "
+ + XML_TAG_SECTION_HEADER_NETWORK_STATUS);
+ }
+ status = NetworkSelectionStatusXmlUtil.parseFromXml(in, outerTagDepth + 1);
+ break;
+ case XML_TAG_SECTION_HEADER_IP_CONFIGURATION:
+ if (ipConfiguration != null) {
+ throw new XmlPullParserException("Detected duplicate tag for: "
+ + XML_TAG_SECTION_HEADER_IP_CONFIGURATION);
+ }
+ ipConfiguration = IpConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1);
+ break;
+ case XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION:
+ if (enterpriseConfig != null) {
+ throw new XmlPullParserException("Detected duplicate tag for: "
+ + XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION);
+ }
+ enterpriseConfig =
+ WifiEnterpriseConfigXmlUtil.parseFromXml(in, outerTagDepth + 1);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown tag under "
+ + XML_TAG_SECTION_HEADER_NETWORK + ": " + headerName[0]);
+ }
+ }
+ 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);
+ }
+
+ configuration.setNetworkSelectionStatus(status);
+ configuration.setIpConfiguration(ipConfiguration);
+ if (enterpriseConfig != null) {
+ configuration.enterpriseConfig = enterpriseConfig;
+ }
+ return configuration;
+ }
+}
+
diff --git a/service/java/com/android/server/wifi/NetworkUpdateResult.java b/service/java/com/android/server/wifi/NetworkUpdateResult.java
index 63cc33f..851b13b 100644
--- a/service/java/com/android/server/wifi/NetworkUpdateResult.java
+++ b/service/java/com/android/server/wifi/NetworkUpdateResult.java
@@ -18,22 +18,25 @@
import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
-class NetworkUpdateResult {
+public class NetworkUpdateResult {
int netId;
boolean ipChanged;
boolean proxyChanged;
+ boolean credentialChanged;
boolean isNewNetwork = false;
public NetworkUpdateResult(int id) {
netId = id;
ipChanged = false;
proxyChanged = false;
+ credentialChanged = false;
}
- public NetworkUpdateResult(boolean ip, boolean proxy) {
+ public NetworkUpdateResult(boolean ip, boolean proxy, boolean credential) {
netId = INVALID_NETWORK_ID;
ipChanged = ip;
proxyChanged = proxy;
+ credentialChanged = credential;
}
public void setNetworkId(int id) {
@@ -44,22 +47,18 @@
return netId;
}
- public void setIpChanged(boolean ip) {
- ipChanged = ip;
- }
-
public boolean hasIpChanged() {
return ipChanged;
}
- public void setProxyChanged(boolean proxy) {
- proxyChanged = proxy;
- }
-
public boolean hasProxyChanged() {
return proxyChanged;
}
+ public boolean hasCredentialChanged() {
+ return credentialChanged;
+ }
+
public boolean isNewNetwork() {
return isNewNetwork;
}
@@ -67,4 +66,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/OWNERS b/service/java/com/android/server/wifi/OWNERS
new file mode 100644
index 0000000..8487100
--- /dev/null
+++ b/service/java/com/android/server/wifi/OWNERS
@@ -0,0 +1,82 @@
+# This file follows the Gerrit find-owners OWNERS syntax described at
+# https://gerrit.googlesource.com/plugins/find-owners/+/master/src/main/resources/Documentation/syntax.md
+
+etancohen@google.com
+kuh@google.com
+lorenzo@google.com
+mett@google.com
+mplass@google.com
+nywang@google.com
+pstew@google.com
+quiche@google.com
+rpius@google.com
+silberst@google.com
+sohanirao@google.com
+zpan@google.com
+zqiu@google.com
+
+# configuration
+per-file WifiBackupRestore*=rpius@google.com
+per-file WifiConfigManager*=rpius@google.com
+per-file WifiConfigStore*=rpius@google.com
+per-file WifiSupplicantControl*=rpius@google.com
+
+# diagnostics
+per-file BaseWifiDiagnostics*=quiche@google.com
+per-file LastMileLogger*=quiche@google.com
+per-file WifiDiagnostics*=quiche@google.com
+per-file WifiLoggerHal*=quiche@google.com
+
+# HAL support
+per-file HalDeviceManager*=etancohen@google.com
+per-file SupplicantStaNetworkHal*=kuh@google.com
+per-file SupplicantStaNetworkHal*=rpius@google.com
+per-file SupplicantStaIfaceHal*=kuh@google.com
+per-file SupplicantStaIfaceHal*=rpius@google.com
+per-file WifiVendorHal*=mplass@google.com
+
+# logging
+per-file DummyLogMessage*=quiche@google.com
+per-file FakeWifiLog*=quiche@google.com
+per-file LogcatLog*=quiche@google.com
+per-file WifiLog*=quiche@google.com
+
+# mode management
+per-file ActiveModeManager*=silberst@google.com
+per-file ClientModeManager*=silberst@google.com
+per-file ScanOnlyModeManager*=silberst@google.com
+per-file WifiController*=silberst@google.com
+per-file WifiService*=silberst@google.com
+per-file WifiServiceImpl*=silberst@google.com
+per-file WifiStateMachine*=silberst@google.com
+
+# network selection
+per-file SavedNetworkEvaluator*=zpan@google.com
+per-file WifiConnectivityManager*=zpan@google.com
+per-file WifiNetworkSelector*=zpan@google.com
+
+# random bits
+per-file ByteBufferReader*=zqiu@google.com
+per-file IMSIParameter*=zqiu@google.com
+per-file SIMAccessor*=zqiu@google.com
+per-file WifiCountryCode*=nywang@google.com
+per-file WifiLastResortWatchdog*=kuh@google.com
+per-file WifiLastResortWatchdog*=silberst@google.com
+per-file WifiMetrics*=kuh@google.com
+per-file WifiScoreReport*=mplass@google.com
+
+# soft-ap/tethering
+per-file SoftApManager*=silberst@google.com
+per-file WifiApConfigStore*=silberst@google.com
+
+# test-support
+per-file Clock*=quiche@google.com
+per-file FrameworkFacade*=kuh@google.com
+per-file FrameworkFacade*=rpius@google.com
+per-file FrameworkFacade*=silberst@google.com
+per-file WifiInjector*=kuh@google.com
+per-file WifiInjector*=rpius@google.com
+per-file WifiInjector*=silberst@google.com
+
+# wificond
+per-file WificondControl*=nywang@google.com
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/RttService.java b/service/java/com/android/server/wifi/RttService.java
index 3eafc77..7e4648b 100644
--- a/service/java/com/android/server/wifi/RttService.java
+++ b/service/java/com/android/server/wifi/RttService.java
@@ -5,36 +5,49 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IClientInterface;
+import android.net.wifi.IInterfaceEventCallback;
import android.net.wifi.IRttManager;
+import android.net.wifi.IWificond;
import android.net.wifi.RttManager;
import android.net.wifi.RttManager.ResponderConfig;
import android.net.wifi.WifiManager;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.IState;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.SystemService;
-import java.util.HashMap;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Queue;
import java.util.Set;
public final class RttService extends SystemService {
public static final boolean DBG = true;
+ private static final String WIFICOND_SERVICE_NAME = "wificond";
static class RttServiceImpl extends IRttManager.Stub {
@@ -65,20 +78,27 @@
if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1);
}
if (DBG) Slog.d(TAG, "closing client " + msg.replyTo);
- ClientInfo ci = mClients.remove(msg.replyTo);
- if (ci != null) ci.cleanup();
+ synchronized (mLock) {
+ ClientInfo ci = mClients.remove(msg.replyTo);
+ if (ci != null) ci.cleanup();
+ }
return;
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
AsyncChannel ac = new AsyncChannel();
ac.connected(mContext, this, msg.replyTo);
- ClientInfo client = new ClientInfo(ac, msg.replyTo);
- mClients.put(msg.replyTo, client);
+ ClientInfo client = new ClientInfo(ac, msg.sendingUid);
+ synchronized (mLock) {
+ mClients.put(msg.replyTo, client);
+ }
ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
AsyncChannel.STATUS_SUCCESSFUL);
return;
}
- ClientInfo ci = mClients.get(msg.replyTo);
+ ClientInfo ci;
+ synchronized (mLock) {
+ ci = mClients.get(msg.replyTo);
+ }
if (ci == null) {
Slog.e(TAG, "Could not find client info for message " + msg.replyTo);
replyFailed(msg, RttManager.REASON_INVALID_LISTENER, "Could not find listener");
@@ -123,17 +143,18 @@
private final Looper mLooper;
private RttStateMachine mStateMachine;
private ClientHandler mClientHandler;
+ private WifiInjector mWifiInjector;
- RttServiceImpl(Context context, Looper looper) {
+ RttServiceImpl(Context context, Looper looper, WifiInjector wifiInjector) {
mContext = context;
- mWifiNative = WifiNative.getWlanNativeInterface();
+ mWifiNative = wifiInjector.getWifiNative();
mLooper = looper;
+ mWifiInjector = wifiInjector;
}
public void startService() {
mClientHandler = new ClientHandler(mLooper);
mStateMachine = new RttStateMachine(mLooper);
-
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
@@ -170,15 +191,15 @@
private class ClientInfo {
private final AsyncChannel mChannel;
- private final Messenger mMessenger;
- HashMap<Integer, RttRequest> mRequests = new HashMap<Integer,
- RttRequest>();
+ private final int mUid;
+
+ ArrayMap<Integer, RttRequest> mRequests = new ArrayMap<>();
// Client keys of all outstanding responders.
Set<Integer> mResponderRequests = new HashSet<>();
- ClientInfo(AsyncChannel c, Messenger m) {
- mChannel = c;
- mMessenger = m;
+ ClientInfo(AsyncChannel channel, int uid) {
+ mChannel = channel;
+ mUid = uid;
}
void addResponderRequest(int key) {
@@ -215,7 +236,7 @@
void reportResponderEnableFailed(int key, int reason) {
mChannel.sendMessage(RttManager.CMD_OP_ENALBE_RESPONDER_FAILED, reason, key);
- mResponderRequests.remove(key);
+ removeResponderRequest(key);
}
void reportResult(RttRequest request, RttManager.RttResult[] results) {
@@ -224,7 +245,7 @@
mChannel.sendMessage(RttManager.CMD_OP_SUCCEEDED,
0, request.key, parcelableResults);
- mRequests.remove(request.key);
+ removeRttRequest(request.key);
}
void reportFailed(RttRequest request, int reason, String description) {
@@ -235,7 +256,7 @@
Bundle bundle = new Bundle();
bundle.putString(RttManager.DESCRIPTION_KEY, description);
mChannel.sendMessage(RttManager.CMD_OP_FAILED, key, reason, bundle);
- mRequests.remove(key);
+ removeRttRequest(key);
}
void reportAborted(int key) {
@@ -252,10 +273,19 @@
mResponderRequests.clear();
mStateMachine.sendMessage(RttManager.CMD_OP_DISABLE_RESPONDER);
}
+
+ @Override
+ public String toString() {
+ return "ClientInfo [uid=" + mUid + ", channel=" + mChannel + "]";
+ }
}
- private Queue<RttRequest> mRequestQueue = new LinkedList<RttRequest>();
- private HashMap<Messenger, ClientInfo> mClients = new HashMap<Messenger, ClientInfo>(4);
+ private Queue<RttRequest> mRequestQueue = new LinkedList<>();
+
+ @GuardedBy("mLock")
+ private ArrayMap<Messenger, ClientInfo> mClients = new ArrayMap<>();
+ // Lock for mClients.
+ private final Object mLock = new Object();
private static final int BASE = Protocol.BASE_WIFI_RTT_SERVICE;
@@ -263,11 +293,36 @@
private static final int CMD_DRIVER_UNLOADED = BASE + 1;
private static final int CMD_ISSUE_NEXT_REQUEST = BASE + 2;
private static final int CMD_RTT_RESPONSE = BASE + 3;
+ private static final int CMD_CLIENT_INTERFACE_READY = BASE + 4;
+ private static final int CMD_CLIENT_INTERFACE_DOWN = BASE + 5;
// Maximum duration for responder role.
private static final int MAX_RESPONDER_DURATION_SECONDS = 60 * 10;
+ private static class InterfaceEventHandler extends IInterfaceEventCallback.Stub {
+ InterfaceEventHandler(RttStateMachine rttStateMachine) {
+ mRttStateMachine = rttStateMachine;
+ }
+ @Override
+ public void OnClientTorndownEvent(IClientInterface networkInterface) {
+ mRttStateMachine.sendMessage(CMD_CLIENT_INTERFACE_DOWN, networkInterface);
+ }
+ @Override
+ public void OnClientInterfaceReady(IClientInterface networkInterface) {
+ mRttStateMachine.sendMessage(CMD_CLIENT_INTERFACE_READY, networkInterface);
+ }
+ @Override
+ public void OnApTorndownEvent(IApInterface networkInterface) { }
+ @Override
+ public void OnApInterfaceReady(IApInterface networkInterface) { }
+
+ private RttStateMachine mRttStateMachine;
+ }
+
class RttStateMachine extends StateMachine {
+ private IWificond mWificond;
+ private InterfaceEventHandler mInterfaceEventHandler;
+ private IClientInterface mClientInterface;
DefaultState mDefaultState = new DefaultState();
EnabledState mEnabledState = new EnabledState();
@@ -305,7 +360,11 @@
case RttManager.CMD_OP_STOP_RANGING:
return HANDLED;
case RttManager.CMD_OP_ENABLE_RESPONDER:
- ClientInfo client = mClients.get(msg.replyTo);
+
+ ClientInfo client;
+ synchronized (mLock) {
+ client = mClients.get(msg.replyTo);
+ }
if (client == null) {
Log.e(TAG, "client not connected yet!");
break;
@@ -325,9 +384,48 @@
class EnabledState extends State {
@Override
+ public void enter() {
+ // This allows us to tolerate wificond restarts.
+ // When wificond restarts WifiStateMachine is supposed to go
+ // back to initial state and restart.
+ // 1) RttService watches for WIFI_STATE_ENABLED broadcasts
+ // 2) WifiStateMachine sends these broadcasts in the SupplicantStarted state
+ // 3) Since WSM will only be in SupplicantStarted for as long as wificond is
+ // alive, we refresh our wificond handler here and we don't subscribe to
+ // wificond's death explicitly.
+ mWificond = mWifiInjector.makeWificond();
+ if (mWificond == null) {
+ Log.w(TAG, "Failed to get wificond binder handler");
+ transitionTo(mDefaultState);
+ }
+ mInterfaceEventHandler = new InterfaceEventHandler(mStateMachine);
+ try {
+ mWificond.RegisterCallback(mInterfaceEventHandler);
+ // Get the current client interface, assuming there is at most
+ // one client interface for now.
+ List<IBinder> interfaces = mWificond.GetClientInterfaces();
+ if (interfaces.size() > 0) {
+ mStateMachine.sendMessage(
+ CMD_CLIENT_INTERFACE_READY,
+ IClientInterface.Stub.asInterface(interfaces.get(0)));
+ }
+ } catch (RemoteException e1) { }
+
+ }
+ @Override
+ public void exit() {
+ try {
+ mWificond.UnregisterCallback(mInterfaceEventHandler);
+ } catch (RemoteException e1) { }
+ mInterfaceEventHandler = null;
+ }
+ @Override
public boolean processMessage(Message msg) {
if (DBG) Log.d(TAG, "EnabledState got" + msg);
- ClientInfo ci = mClients.get(msg.replyTo);
+ ClientInfo ci;
+ synchronized (mLock) {
+ ci = mClients.get(msg.replyTo);
+ }
switch (msg.what) {
case CMD_DRIVER_UNLOADED:
@@ -383,6 +481,14 @@
break;
case RttManager.CMD_OP_DISABLE_RESPONDER:
break;
+ case CMD_CLIENT_INTERFACE_DOWN:
+ if (mClientInterface == (IClientInterface) msg.obj) {
+ mClientInterface = null;
+ }
+ break;
+ case CMD_CLIENT_INTERFACE_READY:
+ mClientInterface = (IClientInterface) msg.obj;
+ break;
default:
return NOT_HANDLED;
}
@@ -455,9 +561,11 @@
// Check if there are still outstanding responder requests from any client.
private boolean hasOutstandingReponderRequests() {
- for (ClientInfo client : mClients.values()) {
- if (!client.mResponderRequests.isEmpty()) {
- return true;
+ synchronized (mLock) {
+ for (ClientInfo client : mClients.values()) {
+ if (!client.mResponderRequests.isEmpty()) {
+ return true;
+ }
}
}
return false;
@@ -470,7 +578,10 @@
@Override
public boolean processMessage(Message msg) {
if (DBG) Log.d(TAG, "ResponderEnabledState got " + msg);
- ClientInfo ci = mClients.get(msg.replyTo);
+ ClientInfo ci;
+ synchronized (mLock) {
+ ci = mClients.get(msg.replyTo);
+ }
int key = msg.arg2;
switch(msg.what) {
case RttManager.CMD_OP_ENABLE_RESPONDER:
@@ -503,6 +614,14 @@
}
}
}
+
+ /**
+ * Returns name of current state.
+ */
+ String currentState() {
+ IState state = getCurrentState();
+ return state == null ? "null" : state.getName();
+ }
}
void replySucceeded(Message msg, Object obj) {
@@ -551,6 +670,26 @@
return true;
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump RttService from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " without permission "
+ + android.Manifest.permission.DUMP);
+ return;
+ }
+ pw.println("current state: " + mStateMachine.currentState());
+ pw.println("clients:");
+ synchronized (mLock) {
+ for (ClientInfo client : mClients.values()) {
+ pw.println(" " + client);
+ }
+ }
+ }
+
private WifiNative.RttEventHandler mEventHandler = new WifiNative.RttEventHandler() {
@Override
public void onRttResults(RttManager.RttResult[] result) {
@@ -597,7 +736,8 @@
@Override
public void onStart() {
- mImpl = new RttServiceImpl(getContext(), mHandlerThread.getLooper());
+ mImpl = new RttServiceImpl(getContext(),
+ mHandlerThread.getLooper(), WifiInjector.getInstance());
Log.i(TAG, "Starting " + Context.WIFI_RTT_SERVICE);
publishBinderService(Context.WIFI_RTT_SERVICE, mImpl);
@@ -608,7 +748,8 @@
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Log.i(TAG, "Registering " + Context.WIFI_RTT_SERVICE);
if (mImpl == null) {
- mImpl = new RttServiceImpl(getContext(), mHandlerThread.getLooper());
+ mImpl = new RttServiceImpl(getContext(),
+ mHandlerThread.getLooper(), WifiInjector.getInstance());
}
mImpl.startService();
}
diff --git a/service/java/com/android/server/wifi/SIMAccessor.java b/service/java/com/android/server/wifi/SIMAccessor.java
index e446436..21bfb9c 100644
--- a/service/java/com/android/server/wifi/SIMAccessor.java
+++ b/service/java/com/android/server/wifi/SIMAccessor.java
@@ -23,7 +23,7 @@
List<String> imsis = new ArrayList<>();
for (int subId : mSubscriptionManager.getActiveSubscriptionIdList()) {
String imsi = mTelephonyManager.getSubscriberId(subId);
- if (mccMnc.matches(imsi)) {
+ if (imsi != null && mccMnc.matchesImsi(imsi)) {
imsis.add(imsi);
}
}
diff --git a/service/java/com/android/server/wifi/SavedNetworkEvaluator.java b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
new file mode 100644
index 0000000..9ac7068
--- /dev/null
+++ b/service/java/com/android/server/wifi/SavedNetworkEvaluator.java
@@ -0,0 +1,342 @@
+/*
+ * 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.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.util.LocalLog;
+import android.util.Pair;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This class is the WifiNetworkSelector.NetworkEvaluator implementation for
+ * saved networks.
+ */
+public class SavedNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator {
+ private static final String NAME = "SavedNetworkEvaluator";
+ private final WifiConfigManager mWifiConfigManager;
+ private final Clock mClock;
+ private final LocalLog mLocalLog;
+ private final WifiConnectivityHelper mConnectivityHelper;
+ private final int mRssiScoreSlope;
+ private final int mRssiScoreOffset;
+ private final int mSameBssidAward;
+ private final int mSameNetworkAward;
+ private final int mBand5GHzAward;
+ private final int mLastSelectionAward;
+ private final int mSecurityAward;
+ private final int mThresholdSaturatedRssi24;
+ private final int mThresholdSaturatedRssi5;
+
+ SavedNetworkEvaluator(final Context context, WifiConfigManager configManager, Clock clock,
+ LocalLog localLog, WifiConnectivityHelper connectivityHelper) {
+ mWifiConfigManager = configManager;
+ mClock = clock;
+ mLocalLog = localLog;
+ mConnectivityHelper = connectivityHelper;
+
+ mRssiScoreSlope = context.getResources().getInteger(
+ R.integer.config_wifi_framework_RSSI_SCORE_SLOPE);
+ mRssiScoreOffset = context.getResources().getInteger(
+ R.integer.config_wifi_framework_RSSI_SCORE_OFFSET);
+ mSameBssidAward = context.getResources().getInteger(
+ R.integer.config_wifi_framework_SAME_BSSID_AWARD);
+ mSameNetworkAward = context.getResources().getInteger(
+ R.integer.config_wifi_framework_current_network_boost);
+ mLastSelectionAward = context.getResources().getInteger(
+ R.integer.config_wifi_framework_LAST_SELECTION_AWARD);
+ mSecurityAward = context.getResources().getInteger(
+ R.integer.config_wifi_framework_SECURITY_AWARD);
+ mBand5GHzAward = context.getResources().getInteger(
+ R.integer.config_wifi_framework_5GHz_preference_boost_factor);
+ mThresholdSaturatedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
+ mThresholdSaturatedRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
+ }
+
+ private void localLog(String log) {
+ mLocalLog.log(log);
+ }
+
+ /**
+ * Get the evaluator name.
+ */
+ public String getName() {
+ return NAME;
+ }
+
+ /**
+ * Update all the saved networks' selection status
+ */
+ private void updateSavedNetworkSelectionStatus() {
+ List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks();
+ if (savedNetworks.size() == 0) {
+ localLog("No saved networks.");
+ return;
+ }
+
+ StringBuffer sbuf = new StringBuffer();
+ for (WifiConfiguration network : savedNetworks) {
+ /**
+ * Ignore Passpoint networks. Passpoint networks are also considered as "saved"
+ * network, but without being persisted to the storage. They are managed
+ * by {@link PasspointNetworkEvaluator}.
+ */
+ if (network.isPasspoint()) {
+ continue;
+ }
+
+ // If a configuration is temporarily disabled, re-enable it before trying
+ // to connect to it.
+ mWifiConfigManager.tryEnableNetwork(network.networkId);
+
+ //TODO(b/30928589): Enable "permanently" disabled networks if we are in DISCONNECTED
+ // state.
+
+ // Clear the cached candidate, score and seen.
+ mWifiConfigManager.clearNetworkCandidateScanResult(network.networkId);
+
+ // Log disabled network.
+ WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus();
+ if (!status.isNetworkEnabled()) {
+ sbuf.append(" ").append(WifiNetworkSelector.toNetworkString(network)).append(" ");
+ for (int index = WifiConfiguration.NetworkSelectionStatus
+ .NETWORK_SELECTION_DISABLED_STARTING_INDEX;
+ index < WifiConfiguration.NetworkSelectionStatus
+ .NETWORK_SELECTION_DISABLED_MAX;
+ index++) {
+ int count = status.getDisableReasonCounter(index);
+ // Here we log the reason as long as its count is greater than zero. The
+ // network may not be disabled because of this particular reason. Logging
+ // this information anyway to help understand what happened to the network.
+ if (count > 0) {
+ sbuf.append("reason=")
+ .append(WifiConfiguration.NetworkSelectionStatus
+ .getNetworkDisableReasonString(index))
+ .append(", count=").append(count).append("; ");
+ }
+ }
+ sbuf.append("\n");
+ }
+ }
+
+ if (sbuf.length() > 0) {
+ localLog("Disabled saved networks:");
+ localLog(sbuf.toString());
+ }
+ }
+
+ /**
+ * Update the evaluator.
+ */
+ public void update(List<ScanDetail> scanDetails) {
+ updateSavedNetworkSelectionStatus();
+ }
+
+ private int calculateBssidScore(ScanResult scanResult, WifiConfiguration network,
+ WifiConfiguration currentNetwork, String currentBssid,
+ StringBuffer sbuf) {
+ int score = 0;
+ boolean is5GHz = scanResult.is5GHz();
+
+ sbuf.append("[ ").append(scanResult.SSID).append(" ").append(scanResult.BSSID)
+ .append(" RSSI:").append(scanResult.level).append(" ] ");
+ // Calculate the RSSI score.
+ int rssiSaturationThreshold = is5GHz ? mThresholdSaturatedRssi5 : mThresholdSaturatedRssi24;
+ int rssi = scanResult.level < rssiSaturationThreshold ? scanResult.level
+ : rssiSaturationThreshold;
+ score += (rssi + mRssiScoreOffset) * mRssiScoreSlope;
+ sbuf.append(" RSSI score: ").append(score).append(",");
+
+ // 5GHz band bonus.
+ if (is5GHz) {
+ score += mBand5GHzAward;
+ sbuf.append(" 5GHz bonus: ").append(mBand5GHzAward).append(",");
+ }
+
+ // Last user selection award.
+ int lastUserSelectedNetworkId = mWifiConfigManager.getLastSelectedNetwork();
+ if (lastUserSelectedNetworkId != WifiConfiguration.INVALID_NETWORK_ID
+ && lastUserSelectedNetworkId == network.networkId) {
+ long timeDifference = mClock.getElapsedSinceBootMillis()
+ - mWifiConfigManager.getLastSelectedTimeStamp();
+ if (timeDifference > 0) {
+ int bonus = mLastSelectionAward - (int) (timeDifference / 1000 / 60);
+ score += bonus > 0 ? bonus : 0;
+ sbuf.append(" User selection ").append(timeDifference / 1000 / 60)
+ .append(" minutes ago, bonus: ").append(bonus).append(",");
+ }
+ }
+
+ // Same network award.
+ if (currentNetwork != null
+ && (network.networkId == currentNetwork.networkId
+ //TODO(b/36788683): re-enable linked configuration check
+ /* || network.isLinked(currentNetwork) */)) {
+ score += mSameNetworkAward;
+ sbuf.append(" Same network bonus: ").append(mSameNetworkAward).append(",");
+
+ // When firmware roaming is supported, equivalent BSSIDs (the ones under the
+ // same network as the currently connected one) get the same BSSID award.
+ if (mConnectivityHelper.isFirmwareRoamingSupported()
+ && currentBssid != null && !currentBssid.equals(scanResult.BSSID)) {
+ score += mSameBssidAward;
+ sbuf.append(" Equivalent BSSID bonus: ").append(mSameBssidAward).append(",");
+ }
+ }
+
+ // Same BSSID award.
+ if (currentBssid != null && currentBssid.equals(scanResult.BSSID)) {
+ score += mSameBssidAward;
+ sbuf.append(" Same BSSID bonus: ").append(mSameBssidAward).append(",");
+ }
+
+ // Security award.
+ if (!WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
+ score += mSecurityAward;
+ sbuf.append(" Secure network bonus: ").append(mSecurityAward).append(",");
+ }
+
+ sbuf.append(" ## Total score: ").append(score).append("\n");
+
+ return score;
+ }
+
+ /**
+ * Evaluate all the networks from the scan results and return
+ * the WifiConfiguration of the network chosen for connection.
+ *
+ * @return configuration of the chosen network;
+ * null if no network in this category is available.
+ */
+ public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
+ WifiConfiguration currentNetwork, String currentBssid, boolean connected,
+ boolean untrustedNetworkAllowed,
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
+ int highestScore = Integer.MIN_VALUE;
+ ScanResult scanResultCandidate = null;
+ WifiConfiguration candidate = null;
+ StringBuffer scoreHistory = new StringBuffer();
+
+ for (ScanDetail scanDetail : scanDetails) {
+ ScanResult scanResult = scanDetail.getScanResult();
+ int highestScoreOfScanResult = Integer.MIN_VALUE;
+ int candidateIdOfScanResult = WifiConfiguration.INVALID_NETWORK_ID;
+
+ // One ScanResult can be associated with more than one networks, hence we calculate all
+ // the scores and use the highest one as the ScanResult's score.
+ List<WifiConfiguration> associatedConfigurations = null;
+ WifiConfiguration associatedConfiguration =
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail);
+
+ if (associatedConfiguration == null) {
+ continue;
+ } else {
+ associatedConfigurations =
+ new ArrayList<>(Arrays.asList(associatedConfiguration));
+ }
+
+ for (WifiConfiguration network : associatedConfigurations) {
+ /**
+ * Ignore Passpoint and Ephemeral networks. They are configured networks,
+ * but without being persisted to the storage. They are evaluated by
+ * {@link PasspointNetworkEvaluator} and {@link ScoredNetworkEvaluator}
+ * respectively.
+ */
+ if (network.isPasspoint() || network.isEphemeral()) {
+ continue;
+ }
+
+ WifiConfiguration.NetworkSelectionStatus status =
+ network.getNetworkSelectionStatus();
+ status.setSeenInLastQualifiedNetworkSelection(true);
+
+ if (!status.isNetworkEnabled()) {
+ continue;
+ } else if (network.BSSID != null && !network.BSSID.equals("any")
+ && !network.BSSID.equals(scanResult.BSSID)) {
+ // App has specified the only BSSID to connect for this
+ // configuration. So only the matching ScanResult can be a candidate.
+ localLog("Network " + WifiNetworkSelector.toNetworkString(network)
+ + " has specified BSSID " + network.BSSID + ". Skip "
+ + scanResult.BSSID);
+ continue;
+ }
+
+ int score = calculateBssidScore(scanResult, network, currentNetwork, currentBssid,
+ scoreHistory);
+
+ // Set candidate ScanResult for all saved networks to ensure that users can
+ // override network selection. See WifiNetworkSelector#setUserConnectChoice.
+ // TODO(b/36067705): consider alternative designs to push filtering/selecting of
+ // user connect choice networks to RecommendedNetworkEvaluator.
+ if (score > status.getCandidateScore() || (score == status.getCandidateScore()
+ && status.getCandidate() != null
+ && scanResult.level > status.getCandidate().level)) {
+ mWifiConfigManager.setNetworkCandidateScanResult(
+ network.networkId, scanResult, score);
+ }
+
+ // If the network is marked to use external scores, or is an open network with
+ // curate saved open networks enabled, do not consider it for network selection.
+ if (network.useExternalScores) {
+ localLog("Network " + WifiNetworkSelector.toNetworkString(network)
+ + " has external score.");
+ continue;
+ }
+
+ if (score > highestScoreOfScanResult) {
+ highestScoreOfScanResult = score;
+ candidateIdOfScanResult = network.networkId;
+ }
+ }
+
+ if (connectableNetworks != null) {
+ connectableNetworks.add(Pair.create(scanDetail,
+ mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult)));
+ }
+
+ if (highestScoreOfScanResult > highestScore
+ || (highestScoreOfScanResult == highestScore
+ && scanResultCandidate != null
+ && scanResult.level > scanResultCandidate.level)) {
+ highestScore = highestScoreOfScanResult;
+ scanResultCandidate = scanResult;
+ mWifiConfigManager.setNetworkCandidateScanResult(
+ candidateIdOfScanResult, scanResultCandidate, highestScore);
+ // Reload the network config with the updated info.
+ candidate = mWifiConfigManager.getConfiguredNetwork(candidateIdOfScanResult);
+ }
+ }
+
+ if (scoreHistory.length() > 0) {
+ localLog("\n" + scoreHistory.toString());
+ }
+
+ if (scanResultCandidate == null) {
+ localLog("did not see any good candidates.");
+ }
+ return candidate;
+ }
+}
diff --git a/service/java/com/android/server/wifi/ScanDetail.java b/service/java/com/android/server/wifi/ScanDetail.java
index dc87a5b..d39888e 100644
--- a/service/java/com/android/server/wifi/ScanDetail.java
+++ b/service/java/com/android/server/wifi/ScanDetail.java
@@ -20,15 +20,13 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiSsid;
-import com.android.server.wifi.anqp.ANQPElement;
-import com.android.server.wifi.anqp.Constants;
-import com.android.server.wifi.anqp.HSFriendlyNameElement;
-import com.android.server.wifi.anqp.RawByteElement;
-import com.android.server.wifi.anqp.VenueNameElement;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+import com.android.server.wifi.hotspot2.anqp.HSFriendlyNameElement;
+import com.android.server.wifi.hotspot2.anqp.RawByteElement;
+import com.android.server.wifi.hotspot2.anqp.VenueNameElement;
import com.android.server.wifi.hotspot2.NetworkDetail;
-import com.android.server.wifi.hotspot2.PasspointMatch;
import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.hotspot2.pps.HomeSP;
import java.util.List;
import java.util.Map;
@@ -39,7 +37,6 @@
public class ScanDetail {
private final ScanResult mScanResult;
private volatile NetworkDetail mNetworkDetail;
- private final Map<HomeSP, PasspointMatch> mMatches;
private long mSeen = 0;
public ScanDetail(NetworkDetail networkDetail, WifiSsid wifiSsid, String bssid,
@@ -50,7 +47,7 @@
networkDetail.getAnqpDomainID(), networkDetail.getOsuProviders(),
caps, level, frequency, tsf);
mSeen = System.currentTimeMillis();
- //mScanResult.seen = mSeen;
+ mScanResult.seen = mSeen;
mScanResult.channelWidth = networkDetail.getChannelWidth();
mScanResult.centerFreq0 = networkDetail.getCenterfreq0();
mScanResult.centerFreq1 = networkDetail.getCenterfreq1();
@@ -62,7 +59,6 @@
if (networkDetail.isInterworking()) {
mScanResult.setFlag(ScanResult.FLAG_PASSPOINT_NETWORK);
}
- mMatches = null;
}
public ScanDetail(WifiSsid wifiSsid, String bssid, String caps, int level, int frequency,
@@ -70,53 +66,19 @@
mNetworkDetail = null;
mScanResult = new ScanResult(wifiSsid, bssid, 0L, -1, null, caps, level, frequency, tsf);
mSeen = seen;
- //mScanResult.seen = mSeen;
+ mScanResult.seen = mSeen;
mScanResult.channelWidth = 0;
mScanResult.centerFreq0 = 0;
mScanResult.centerFreq1 = 0;
mScanResult.flags = 0;
- mMatches = null;
}
- public ScanDetail(ScanResult scanResult, NetworkDetail networkDetail,
- Map<HomeSP, PasspointMatch> matches) {
+ public ScanDetail(ScanResult scanResult, NetworkDetail networkDetail) {
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..a651379 100644
--- a/service/java/com/android/server/wifi/ScanDetailCache.java
+++ b/service/java/com/android/server/wifi/ScanDetailCache.java
@@ -21,16 +21,11 @@
import android.os.SystemClock;
import android.util.Log;
-import com.android.server.wifi.hotspot2.PasspointMatch;
-import com.android.server.wifi.hotspot2.PasspointMatchInfo;
-import com.android.server.wifi.hotspot2.pps.HomeSP;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.HashMap;
/**
* Maps BSSIDs to their individual ScanDetails for a given WifiConfiguration.
@@ -40,28 +35,36 @@
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;
- 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);
}
void put(ScanDetail scanDetail) {
- put(scanDetail, null, null);
- }
-
- 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);
-
- if (match != null && homeSp != null) {
- mPasspointMatches.put(scanDetail.getBSSIDString(),
- new PasspointMatchInfo(match, scanDetail, homeSp));
- }
}
ScanResult get(String bssid) {
@@ -94,13 +97,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,11 +122,10 @@
}
});
}
- 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());
- mPasspointMatches.remove(result.getBSSIDString());
}
}
@@ -219,60 +220,6 @@
}
/**
- * Method returning the Visibility based on passpoint match time.
- *
- * @param age long Desired time window for matches.
- * @return WifiConfiguration.Visibility matches in the given visibility
- */
- public WifiConfiguration.Visibility getVisibilityByPasspointMatch(long age) {
-
- long now_ms = System.currentTimeMillis();
- PasspointMatchInfo pmiBest24 = null, pmiBest5 = null;
-
- for (PasspointMatchInfo pmi : mPasspointMatches.values()) {
- ScanDetail scanDetail = pmi.getScanDetail();
- if (scanDetail == null) continue;
- ScanResult result = scanDetail.getScanResult();
- if (result == null) continue;
-
- if (scanDetail.getSeen() == 0) continue;
-
- if ((now_ms - result.seen) > age) continue;
-
- if (result.is5GHz()) {
- if (pmiBest5 == null || pmiBest5.compareTo(pmi) < 0) {
- pmiBest5 = pmi;
- }
- } else if (result.is24GHz()) {
- if (pmiBest24 == null || pmiBest24.compareTo(pmi) < 0) {
- pmiBest24 = pmi;
- }
- }
- }
-
- WifiConfiguration.Visibility status = new WifiConfiguration.Visibility();
- String logMsg = "Visiblity by passpoint match returned ";
- if (pmiBest5 != null) {
- ScanResult result = pmiBest5.getScanDetail().getScanResult();
- status.rssi5 = result.level;
- status.age5 = result.seen;
- status.BSSID5 = result.BSSID;
- logMsg += "5 GHz BSSID of " + result.BSSID;
- }
- if (pmiBest24 != null) {
- ScanResult result = pmiBest24.getScanDetail().getScanResult();
- status.rssi24 = result.level;
- status.age24 = result.seen;
- status.BSSID24 = result.BSSID;
- logMsg += "2.4 GHz BSSID of " + result.BSSID;
- }
-
- Log.d(TAG, logMsg);
-
- return status;
- }
-
- /**
* Method to get scan matches for the desired time window. Returns matches by passpoint time if
* the WifiConfiguration is passpoint.
*
@@ -280,15 +227,10 @@
* @return WifiConfiguration.Visibility matches in the given visibility
*/
public WifiConfiguration.Visibility getVisibility(long age) {
- if (mConfig.isPasspoint()) {
- return getVisibilityByPasspointMatch(age);
- } else {
- return getVisibilityByRssi(age);
- }
+ return getVisibilityByRssi(age);
}
-
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
diff --git a/service/java/com/android/server/wifi/ScanOnlyModeManager.java b/service/java/com/android/server/wifi/ScanOnlyModeManager.java
new file mode 100644
index 0000000..1edb857
--- /dev/null
+++ b/service/java/com/android/server/wifi/ScanOnlyModeManager.java
@@ -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.
+ */
+
+package com.android.server.wifi;
+
+/**
+ * Manager WiFi in Scan Only Mode - no network connections.
+ */
+public class ScanOnlyModeManager implements ActiveModeManager {
+
+ private static final String TAG = "ScanOnlyModeManager";
+
+ ScanOnlyModeManager() {
+ }
+
+ /**
+ * Start scan only mode.
+ */
+ public void start() {
+
+ }
+
+ /**
+ * Cancel any pending scans and stop scan mode.
+ */
+ public void stop() {
+
+ }
+}
diff --git a/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java b/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java
new file mode 100644
index 0000000..483da99
--- /dev/null
+++ b/service/java/com/android/server/wifi/ScoredNetworkEvaluator.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiNetworkScoreCache;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.server.wifi.util.ScanResultUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link WifiNetworkSelector.NetworkEvaluator} implementation that uses scores obtained by
+ * {@link NetworkScoreManager#requestScores(NetworkKey[])} to make network connection decisions.
+ */
+public class ScoredNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator {
+ private static final String TAG = "ScoredNetworkEvaluator";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final NetworkScoreManager mNetworkScoreManager;
+ private final WifiConfigManager mWifiConfigManager;
+ private final LocalLog mLocalLog;
+ private final ContentObserver mContentObserver;
+ private boolean mNetworkRecommendationsEnabled;
+ private WifiNetworkScoreCache mScoreCache;
+
+ ScoredNetworkEvaluator(final Context context, Looper looper,
+ final FrameworkFacade frameworkFacade, NetworkScoreManager networkScoreManager,
+ WifiConfigManager wifiConfigManager, LocalLog localLog,
+ WifiNetworkScoreCache wifiNetworkScoreCache) {
+ mScoreCache = wifiNetworkScoreCache;
+ mNetworkScoreManager = networkScoreManager;
+ mWifiConfigManager = wifiConfigManager;
+ mLocalLog = localLog;
+ mContentObserver = new ContentObserver(new Handler(looper)) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mNetworkRecommendationsEnabled = frameworkFacade.getIntegerSetting(context,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) == 1;
+ }
+ };
+ frameworkFacade.registerContentObserver(context,
+ Settings.Global.getUriFor(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED),
+ false /* notifyForDescendents */, mContentObserver);
+ mContentObserver.onChange(false /* unused */);
+ mLocalLog.log("ScoredNetworkEvaluator constructed. mNetworkRecommendationsEnabled: "
+ + mNetworkRecommendationsEnabled);
+ }
+
+ @Override
+ public void update(List<ScanDetail> scanDetails) {
+ if (mNetworkRecommendationsEnabled) {
+ updateNetworkScoreCache(scanDetails);
+ }
+ }
+
+ private void updateNetworkScoreCache(List<ScanDetail> scanDetails) {
+ ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>();
+ for (int i = 0; i < scanDetails.size(); i++) {
+ ScanResult scanResult = scanDetails.get(i).getScanResult();
+ NetworkKey networkKey = NetworkKey.createFromScanResult(scanResult);
+ if (networkKey != null) {
+ // Is there a ScoredNetwork for this ScanResult? If not, request a score.
+ if (mScoreCache.getScoredNetwork(networkKey) == null) {
+ unscoredNetworks.add(networkKey);
+ }
+ }
+ }
+
+ // Kick the score manager if there are any unscored network.
+ if (!unscoredNetworks.isEmpty()) {
+ NetworkKey[] unscoredNetworkKeys =
+ unscoredNetworks.toArray(new NetworkKey[unscoredNetworks.size()]);
+ mNetworkScoreManager.requestScores(unscoredNetworkKeys);
+ }
+ }
+
+ @Override
+ public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
+ WifiConfiguration currentNetwork, String currentBssid, boolean connected,
+ boolean untrustedNetworkAllowed,
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
+ if (!mNetworkRecommendationsEnabled) {
+ mLocalLog.log("Skipping evaluateNetworks; Network recommendations disabled.");
+ return null;
+ }
+
+ final ScoreTracker scoreTracker = new ScoreTracker();
+ for (int i = 0; i < scanDetails.size(); i++) {
+ ScanDetail scanDetail = scanDetails.get(i);
+ ScanResult scanResult = scanDetail.getScanResult();
+ if (scanResult == null) continue;
+ if (mWifiConfigManager.wasEphemeralNetworkDeleted(
+ ScanResultUtil.createQuotedSSID(scanResult.SSID))) {
+ debugLog("Ignoring disabled ephemeral SSID: " + scanResult.SSID);
+ continue;
+ }
+ final WifiConfiguration configuredNetwork =
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail);
+ boolean untrustedScanResult = configuredNetwork == null || configuredNetwork.ephemeral;
+
+ if (!untrustedNetworkAllowed && untrustedScanResult) {
+ continue;
+ }
+
+ // Track scan results for open wifi networks
+ if (configuredNetwork == null) {
+ if (ScanResultUtil.isScanResultForOpenNetwork(scanResult)) {
+ scoreTracker.trackUntrustedCandidate(scanResult);
+ }
+ continue;
+ }
+
+ // Ignore non-ephemeral and non-externally scored networks
+ if (!configuredNetwork.ephemeral && !configuredNetwork.useExternalScores) {
+ continue;
+ }
+
+ // Ignore externally scored or ephemeral networks that have been disabled for selection
+ if (!configuredNetwork.getNetworkSelectionStatus().isNetworkEnabled()) {
+ debugLog("Ignoring disabled SSID: " + configuredNetwork.SSID);
+ continue;
+ }
+
+ // TODO(b/37485956): consider applying a boost for networks with only the same SSID
+ boolean isCurrentNetwork = currentNetwork != null
+ && currentNetwork.networkId == configuredNetwork.networkId
+ && TextUtils.equals(currentBssid, scanResult.BSSID);
+ if (configuredNetwork.ephemeral) {
+ scoreTracker.trackUntrustedCandidate(
+ scanResult, configuredNetwork, isCurrentNetwork);
+ } else {
+ scoreTracker.trackExternallyScoredCandidate(
+ scanResult, configuredNetwork, isCurrentNetwork);
+ }
+ if (connectableNetworks != null) {
+ connectableNetworks.add(Pair.create(scanDetail, configuredNetwork));
+ }
+ }
+
+ return scoreTracker.getCandidateConfiguration();
+ }
+
+ /** Used to track the network with the highest score. */
+ class ScoreTracker {
+ private static final int EXTERNAL_SCORED_NONE = 0;
+ private static final int EXTERNAL_SCORED_SAVED_NETWORK = 1;
+ private static final int EXTERNAL_SCORED_UNTRUSTED_NETWORK = 2;
+
+ private int mBestCandidateType = EXTERNAL_SCORED_NONE;
+ private int mHighScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE;
+ private WifiConfiguration mEphemeralConfig;
+ private WifiConfiguration mSavedConfig;
+ private ScanResult mScanResultCandidate;
+
+ /**
+ * Returns the available external network score or null if no score is available.
+ *
+ * @param scanResult The scan result of the network to score.
+ * @param isCurrentNetwork Flag which indicates whether this is the current network.
+ * @return A valid external score if one is available or NULL.
+ */
+ @Nullable
+ private Integer getNetworkScore(ScanResult scanResult, boolean isCurrentNetwork) {
+ if (mScoreCache.isScoredNetwork(scanResult)) {
+ int score = mScoreCache.getNetworkScore(scanResult, isCurrentNetwork);
+ if (DEBUG) {
+ mLocalLog.log(WifiNetworkSelector.toScanId(scanResult) + " has score: "
+ + score + " isCurrentNetwork network: " + isCurrentNetwork);
+ }
+ return score;
+ }
+ return null;
+ }
+
+ /** Track an untrusted {@link ScanResult}. */
+ void trackUntrustedCandidate(ScanResult scanResult) {
+ Integer score = getNetworkScore(scanResult, false /* isCurrentNetwork */);
+ if (score != null && score > mHighScore) {
+ mHighScore = score;
+ mScanResultCandidate = scanResult;
+ mBestCandidateType = EXTERNAL_SCORED_UNTRUSTED_NETWORK;
+ debugLog(WifiNetworkSelector.toScanId(scanResult)
+ + " becomes the new untrusted candidate.");
+ }
+ }
+
+ /**
+ * Track an untrusted {@link ScanResult} that already has a corresponding
+ * ephemeral {@link WifiConfiguration}.
+ */
+ void trackUntrustedCandidate(
+ ScanResult scanResult, WifiConfiguration config, boolean isCurrentNetwork) {
+ Integer score = getNetworkScore(scanResult, isCurrentNetwork);
+ if (score != null && score > mHighScore) {
+ mHighScore = score;
+ mScanResultCandidate = scanResult;
+ mBestCandidateType = EXTERNAL_SCORED_UNTRUSTED_NETWORK;
+ mEphemeralConfig = config;
+ mWifiConfigManager.setNetworkCandidateScanResult(config.networkId, scanResult, 0);
+ debugLog(WifiNetworkSelector.toScanId(scanResult)
+ + " becomes the new untrusted candidate.");
+ }
+ }
+
+ /** Tracks a saved network that has been marked with useExternalScores */
+ void trackExternallyScoredCandidate(
+ ScanResult scanResult, WifiConfiguration config, boolean isCurrentNetwork) {
+ // Always take the highest score. If there's a tie and an untrusted network is currently
+ // the best then pick the saved network.
+ Integer score = getNetworkScore(scanResult, isCurrentNetwork);
+ if (score != null
+ && (score > mHighScore
+ || (mBestCandidateType == EXTERNAL_SCORED_UNTRUSTED_NETWORK
+ && score == mHighScore))) {
+ mHighScore = score;
+ mSavedConfig = config;
+ mScanResultCandidate = scanResult;
+ mBestCandidateType = EXTERNAL_SCORED_SAVED_NETWORK;
+ mWifiConfigManager.setNetworkCandidateScanResult(config.networkId, scanResult, 0);
+ debugLog(WifiNetworkSelector.toScanId(scanResult)
+ + " becomes the new externally scored saved network candidate.");
+ }
+ }
+
+ /** Returns the best candidate network tracked by this {@link ScoreTracker}. */
+ @Nullable
+ WifiConfiguration getCandidateConfiguration() {
+ int candidateNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
+ switch (mBestCandidateType) {
+ case ScoreTracker.EXTERNAL_SCORED_UNTRUSTED_NETWORK:
+ if (mEphemeralConfig != null) {
+ candidateNetworkId = mEphemeralConfig.networkId;
+ mLocalLog.log(String.format("existing ephemeral candidate %s network ID:%d"
+ + ", meteredHint=%b",
+ WifiNetworkSelector.toScanId(mScanResultCandidate),
+ candidateNetworkId,
+ mEphemeralConfig.meteredHint));
+ break;
+ }
+
+ mEphemeralConfig =
+ ScanResultUtil.createNetworkFromScanResult(mScanResultCandidate);
+ // Mark this config as ephemeral so it isn't persisted.
+ mEphemeralConfig.ephemeral = true;
+ mEphemeralConfig.meteredHint = mScoreCache.getMeteredHint(mScanResultCandidate);
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(mEphemeralConfig,
+ Process.WIFI_UID);
+ if (!result.isSuccess()) {
+ mLocalLog.log("Failed to add ephemeral network");
+ break;
+ }
+ if (!mWifiConfigManager.updateNetworkSelectionStatus(result.getNetworkId(),
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE)) {
+ mLocalLog.log("Failed to make ephemeral network selectable");
+ break;
+ }
+ candidateNetworkId = result.getNetworkId();
+ mWifiConfigManager.setNetworkCandidateScanResult(candidateNetworkId,
+ mScanResultCandidate, 0);
+ mLocalLog.log(String.format("new ephemeral candidate %s network ID:%d, "
+ + "meteredHint=%b",
+ WifiNetworkSelector.toScanId(mScanResultCandidate),
+ candidateNetworkId,
+ mEphemeralConfig.meteredHint));
+ break;
+ case ScoreTracker.EXTERNAL_SCORED_SAVED_NETWORK:
+ candidateNetworkId = mSavedConfig.networkId;
+ mLocalLog.log(String.format("new saved network candidate %s network ID:%d",
+ WifiNetworkSelector.toScanId(mScanResultCandidate),
+ candidateNetworkId));
+ break;
+ case ScoreTracker.EXTERNAL_SCORED_NONE:
+ default:
+ mLocalLog.log("ScoredNetworkEvaluator did not see any good candidates.");
+ break;
+ }
+ return mWifiConfigManager.getConfiguredNetwork(candidateNetworkId);
+ }
+ }
+
+ private void debugLog(String msg) {
+ if (DEBUG) {
+ mLocalLog.log(msg);
+ }
+ }
+
+ @Override
+ public String getName() {
+ return TAG;
+ }
+}
diff --git a/service/java/com/android/server/wifi/SelfRecovery.java b/service/java/com/android/server/wifi/SelfRecovery.java
new file mode 100644
index 0000000..b35b7cc
--- /dev/null
+++ b/service/java/com/android/server/wifi/SelfRecovery.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.util.Log;
+
+/**
+ * This class is used to recover the wifi stack from a fatal failure. The recovery mechanism
+ * involves triggering a stack restart (essentially simulating an airplane mode toggle) using
+ * {@link WifiController}.
+ * The current triggers for:
+ * 1. Last resort watchdog bite.
+ * 2. HAL/wificond crashes during normal operation.
+ * 3. TBD: supplicant crashes during normal operation.
+ */
+public class SelfRecovery {
+ private static final String TAG = "WifiSelfRecovery";
+
+ /**
+ * Reason codes for the various recovery triggers.
+ */
+ public static final int REASON_LAST_RESORT_WATCHDOG = 0;
+ public static final int REASON_HAL_CRASH = 1;
+ public static final int REASON_WIFICOND_CRASH = 2;
+
+ private static final String[] REASON_STRINGS = {
+ "Last Resort Watchdog", // REASON_LAST_RESORT_WATCHDOG
+ "Hal Crash", // REASON_HAL_CRASH
+ "Wificond Crash" // REASON_WIFICOND_CRASH
+ };
+
+ private final WifiController mWifiController;
+
+ SelfRecovery(WifiController wifiController) {
+ mWifiController = wifiController;
+ }
+
+ /**
+ * Trigger recovery.
+ *
+ * This method does the following:
+ * 1. Raises a wtf.
+ * 2. Sends {@link WifiController#CMD_RESTART_WIFI} to {@link WifiController} to initiate the
+ * stack restart.
+ * @param reason One of the above |REASON_*| codes.
+ */
+ public void trigger(int reason) {
+ if (reason < REASON_LAST_RESORT_WATCHDOG || reason > REASON_WIFICOND_CRASH) {
+ Log.e(TAG, "Invalid trigger reason. Ignoring...");
+ return;
+ }
+ Log.wtf(TAG, "Triggering recovery for reason: " + REASON_STRINGS[reason]);
+ mWifiController.sendMessage(WifiController.CMD_RESTART_WIFI);
+ }
+}
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 2dfb754..64f4a14 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -20,41 +20,49 @@
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.InterfaceConfiguration;
+import android.net.wifi.IApInterface;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiManager;
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;
import com.android.internal.util.StateMachine;
+import com.android.server.net.BaseNetworkObserver;
import com.android.server.wifi.util.ApConfigUtil;
-import java.util.ArrayList;
+import java.nio.charset.StandardCharsets;
import java.util.Locale;
/**
* Manage WiFi in AP mode.
* The internal state machine runs under "WifiStateMachine" thread context.
*/
-public class SoftApManager {
+public class SoftApManager implements ActiveModeManager {
private static final String TAG = "SoftApManager";
- private final INetworkManagementService mNmService;
private final WifiNative mWifiNative;
- private final ArrayList<Integer> mAllowed2GChannels;
private final String mCountryCode;
- private final String mInterfaceName;
-
private final SoftApStateMachine mStateMachine;
private final Listener mListener;
+ private final IApInterface mApInterface;
+
+ private final INetworkManagementService mNwService;
+ private final WifiApConfigStore mWifiApConfigStore;
+
+ private final WifiMetrics mWifiMetrics;
+
+ private WifiConfiguration mApConfig;
+
/**
* Listener for soft AP state changes.
*/
@@ -69,27 +77,34 @@
public SoftApManager(Looper looper,
WifiNative wifiNative,
- INetworkManagementService nmService,
String countryCode,
- ArrayList<Integer> allowed2GChannels,
- Listener listener) {
+ Listener listener,
+ IApInterface apInterface,
+ INetworkManagementService nms,
+ WifiApConfigStore wifiApConfigStore,
+ WifiConfiguration config,
+ WifiMetrics wifiMetrics) {
mStateMachine = new SoftApStateMachine(looper);
- mNmService = nmService;
mWifiNative = wifiNative;
mCountryCode = countryCode;
- mAllowed2GChannels = allowed2GChannels;
mListener = listener;
-
- mInterfaceName = mWifiNative.getInterfaceName();
+ mApInterface = apInterface;
+ mNwService = nms;
+ mWifiApConfigStore = wifiApConfigStore;
+ if (config == null) {
+ mApConfig = mWifiApConfigStore.getApConfiguration();
+ } else {
+ mApConfig = config;
+ }
+ mWifiMetrics = wifiMetrics;
}
/**
- * Start soft AP with given configuration.
- * @param config AP configuration
+ * Start soft AP with the supplied config.
*/
- public void start(WifiConfiguration config) {
- mStateMachine.sendMessage(SoftApStateMachine.CMD_START, config);
+ public void start() {
+ mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig);
}
/**
@@ -116,27 +131,26 @@
* @return integer result code
*/
private int startSoftAp(WifiConfiguration config) {
- if (config == null) {
- Log.e(TAG, "Unable to start soft AP without configuration");
+ if (config == null || config.SSID == null) {
+ Log.e(TAG, "Unable to start soft AP without valid configuration");
return ERROR_GENERIC;
}
- /* Make a copy of configuration for updating AP band and channel. */
+ // Make a copy of configuration for updating AP band and channel.
WifiConfiguration localConfig = new WifiConfiguration(config);
int result = ApConfigUtil.updateApChannelConfig(
- mWifiNative, mCountryCode, mAllowed2GChannels, localConfig);
+ mWifiNative, mCountryCode,
+ mWifiApConfigStore.getAllowed2GChannel(), localConfig);
if (result != SUCCESS) {
Log.e(TAG, "Failed to update AP band and channel");
return result;
}
- /* Setup country code if it is provide. */
+ // Setup country code if it is provided.
if (mCountryCode != null) {
- /**
- * Country code is mandatory for 5GHz band, return an error if failed to set
- * country code when AP is configured for 5GHz band.
- */
+ // Country code is mandatory for 5GHz band, return an error if failed to set
+ // country code when AP is configured for 5GHz band.
if (!mWifiNative.setCountryCodeHal(mCountryCode.toUpperCase(Locale.ROOT))
&& config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
Log.e(TAG, "Failed to set country code, required for setting up "
@@ -145,11 +159,30 @@
}
}
+ int encryptionType = getIApInterfaceEncryptionType(localConfig);
+
try {
- mNmService.startAccessPoint(localConfig, mInterfaceName);
- } catch (Exception e) {
+ // Note that localConfig.SSID is intended to be either a hex string or "double quoted".
+ // However, it seems that whatever is handing us these configurations does not obey
+ // this convention.
+ boolean success = mApInterface.writeHostapdConfig(
+ localConfig.SSID.getBytes(StandardCharsets.UTF_8), false,
+ localConfig.apChannel, encryptionType,
+ (localConfig.preSharedKey != null)
+ ? localConfig.preSharedKey.getBytes(StandardCharsets.UTF_8)
+ : new byte[0]);
+ if (!success) {
+ Log.e(TAG, "Failed to write hostapd configuration");
+ return ERROR_GENERIC;
+ }
+
+ success = mApInterface.startHostapd();
+ if (!success) {
+ Log.e(TAG, "Failed to start hostapd.");
+ return ERROR_GENERIC;
+ }
+ } catch (RemoteException e) {
Log.e(TAG, "Exception in starting soft AP: " + e);
- return ERROR_GENERIC;
}
Log.d(TAG, "Soft AP is started");
@@ -157,13 +190,34 @@
return SUCCESS;
}
+ private static int getIApInterfaceEncryptionType(WifiConfiguration localConfig) {
+ int encryptionType;
+ switch (localConfig.getAuthType()) {
+ case KeyMgmt.NONE:
+ encryptionType = IApInterface.ENCRYPTION_TYPE_NONE;
+ break;
+ case KeyMgmt.WPA_PSK:
+ encryptionType = IApInterface.ENCRYPTION_TYPE_WPA;
+ break;
+ case KeyMgmt.WPA2_PSK:
+ encryptionType = IApInterface.ENCRYPTION_TYPE_WPA2;
+ break;
+ default:
+ // We really shouldn't default to None, but this was how NetworkManagementService
+ // used to do this.
+ encryptionType = IApInterface.ENCRYPTION_TYPE_NONE;
+ break;
+ }
+ return encryptionType;
+ }
+
/**
* Teardown soft AP.
*/
private void stopSoftAp() {
try {
- mNmService.stopAccessPoint(mInterfaceName);
- } catch (Exception e) {
+ mApInterface.stopHostapd();
+ } catch (RemoteException e) {
Log.e(TAG, "Exception in stopping soft AP: " + e);
return;
}
@@ -171,18 +225,41 @@
}
private class SoftApStateMachine extends StateMachine {
- /* Commands for the state machine. */
+ // 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;
+ public static final int CMD_INTERFACE_STATUS_CHANGED = 3;
private final State mIdleState = new IdleState();
private final State mStartedState = new StartedState();
+ private final StateMachineDeathRecipient mDeathRecipient =
+ new StateMachineDeathRecipient(this, CMD_AP_INTERFACE_BINDER_DEATH);
+
+ private NetworkObserver mNetworkObserver;
+
+ private class NetworkObserver extends BaseNetworkObserver {
+ private final String mIfaceName;
+
+ NetworkObserver(String ifaceName) {
+ mIfaceName = ifaceName;
+ }
+
+ @Override
+ public void interfaceLinkStateChanged(String iface, boolean up) {
+ if (mIfaceName.equals(iface)) {
+ SoftApStateMachine.this.sendMessage(
+ CMD_INTERFACE_STATUS_CHANGED, up ? 1 : 0, 0, this);
+ }
+ }
+ }
+
SoftApStateMachine(Looper looper) {
super(TAG, looper);
addState(mIdleState);
- addState(mStartedState, mIdleState);
+ addState(mStartedState);
setInitialState(mIdleState);
start();
@@ -190,41 +267,126 @@
private class IdleState extends State {
@Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START:
- updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 0);
- int result = startSoftAp((WifiConfiguration) message.obj);
- if (result == SUCCESS) {
- updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0);
- transitionTo(mStartedState);
- } else {
- int reason = WifiManager.SAP_START_FAILURE_GENERAL;
- if (result == ERROR_NO_CHANNEL) {
- reason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
- }
- updateApState(WifiManager.WIFI_AP_STATE_FAILED, reason);
- }
- break;
- default:
- /* Ignore all other commands. */
- break;
- }
- return HANDLED;
+ public void enter() {
+ mDeathRecipient.unlinkToDeath();
+ unregisterObserver();
}
- }
- private class StartedState extends State {
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START:
- /* Already started, ignore this command. */
+ updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 0);
+ if (!mDeathRecipient.linkToDeath(mApInterface.asBinder())) {
+ mDeathRecipient.unlinkToDeath();
+ updateApState(WifiManager.WIFI_AP_STATE_FAILED,
+ WifiManager.SAP_START_FAILURE_GENERAL);
+ mWifiMetrics.incrementSoftApStartResult(
+ false, WifiManager.SAP_START_FAILURE_GENERAL);
+ break;
+ }
+
+ try {
+ mNetworkObserver = new NetworkObserver(mApInterface.getInterfaceName());
+ mNwService.registerObserver(mNetworkObserver);
+ } catch (RemoteException e) {
+ mDeathRecipient.unlinkToDeath();
+ unregisterObserver();
+ updateApState(WifiManager.WIFI_AP_STATE_FAILED,
+ WifiManager.SAP_START_FAILURE_GENERAL);
+ mWifiMetrics.incrementSoftApStartResult(
+ false, WifiManager.SAP_START_FAILURE_GENERAL);
+ break;
+ }
+
+ int result = startSoftAp((WifiConfiguration) message.obj);
+ if (result != SUCCESS) {
+ int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
+ if (result == ERROR_NO_CHANNEL) {
+ failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
+ }
+ mDeathRecipient.unlinkToDeath();
+ unregisterObserver();
+ updateApState(WifiManager.WIFI_AP_STATE_FAILED, failureReason);
+ mWifiMetrics.incrementSoftApStartResult(false, failureReason);
+ break;
+ }
+
+ transitionTo(mStartedState);
break;
+ default:
+ // Ignore all other commands.
+ break;
+ }
+
+ return HANDLED;
+ }
+
+ private void unregisterObserver() {
+ if (mNetworkObserver == null) {
+ return;
+ }
+ try {
+ mNwService.unregisterObserver(mNetworkObserver);
+ } catch (RemoteException e) { }
+ mNetworkObserver = null;
+ }
+ }
+
+ private class StartedState extends State {
+ private boolean mIfaceIsUp;
+
+ private void onUpChanged(boolean isUp) {
+ if (isUp == mIfaceIsUp) {
+ return; // no change
+ }
+ mIfaceIsUp = isUp;
+ if (isUp) {
+ Log.d(TAG, "SoftAp is ready for use");
+ updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0);
+ mWifiMetrics.incrementSoftApStartResult(true, 0);
+ } else {
+ // TODO: handle the case where the interface was up, but goes down
+ }
+ }
+
+ @Override
+ public void enter() {
+ mIfaceIsUp = false;
+ InterfaceConfiguration config = null;
+ try {
+ config = mNwService.getInterfaceConfig(mApInterface.getInterfaceName());
+ } catch (RemoteException e) {
+ }
+ if (config != null) {
+ onUpChanged(config.isUp());
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case CMD_INTERFACE_STATUS_CHANGED:
+ if (message.obj != mNetworkObserver) {
+ // This is from some time before the most recent configuration.
+ break;
+ }
+ boolean isUp = message.arg1 == 1;
+ onUpChanged(isUp);
+ break;
+ 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/SoftApModeConfiguration.java b/service/java/com/android/server/wifi/SoftApModeConfiguration.java
new file mode 100644
index 0000000..e880560
--- /dev/null
+++ b/service/java/com/android/server/wifi/SoftApModeConfiguration.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.net.wifi.WifiConfiguration;
+
+/**
+ * Object holding the parameters needed to start SoftAp mode.
+ *
+ * Initially, this will hold the WifiConfiguration and mode.
+ */
+public class SoftApModeConfiguration {
+ final int mTargetMode;
+ final WifiConfiguration mConfig;
+
+ SoftApModeConfiguration(int targetMode, WifiConfiguration config) {
+ mTargetMode = targetMode;
+ mConfig = config;
+ }
+
+ public int getTargetMode() {
+ return mTargetMode;
+ }
+
+ public WifiConfiguration getWifiConfiguration() {
+ return mConfig;
+ }
+}
diff --git a/service/java/com/android/server/wifi/StateMachineDeathRecipient.java b/service/java/com/android/server/wifi/StateMachineDeathRecipient.java
new file mode 100644
index 0000000..64c4bee
--- /dev/null
+++ b/service/java/com/android/server/wifi/StateMachineDeathRecipient.java
@@ -0,0 +1,86 @@
+/*
+ * 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.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import com.android.internal.util.StateMachine;
+
+/**
+ * Allows StateMachine instances to subscribe to binder death.
+ *
+ * @hide
+ */
+public class StateMachineDeathRecipient implements DeathRecipient {
+
+ private final StateMachine mStateMachine;
+ private final int mDeathCommand;
+ private IBinder mLinkedBinder;
+
+ /**
+ * Construct a StateMachineDeathRecipient.
+ *
+ * @param sm StateMachine instance to receive a message upon Binder death.
+ * @param command message to send the state machine.
+ */
+ public StateMachineDeathRecipient(StateMachine sm, int command) {
+ mStateMachine = sm;
+ mDeathCommand = command;
+ }
+
+ /**
+ * Listen for the death of a binder.
+ *
+ * This method will unlink from death notifications from any
+ * previously linked IBinder instance.
+ *
+ * @param binder remote object to listen for death.
+ * @return true iff we have successfully subscribed to death notifications of a live
+ * IBinder instance.
+ */
+ public boolean linkToDeath(IBinder binder) {
+ unlinkToDeath();
+ try {
+ binder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ // The remote has already died.
+ return false;
+ }
+ mLinkedBinder = binder;
+ return true;
+ }
+
+ /**
+ * Unlink from notifications from the last linked IBinder instance.
+ */
+ public void unlinkToDeath() {
+ if (mLinkedBinder == null) {
+ return;
+ }
+ mLinkedBinder.unlinkToDeath(this, 0);
+ mLinkedBinder = null;
+ }
+
+ /**
+ * Called by the binder subsystem upon remote object death.
+ */
+ @Override
+ public void binderDied() {
+ mStateMachine.sendMessage(mDeathCommand);
+ }
+}
\ No newline at end of file
diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
new file mode 100644
index 0000000..e9c20db
--- /dev/null
+++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
@@ -0,0 +1,2065 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wifi;
+
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQP3GPPNetwork;
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPDomName;
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPIPAddrAvailability;
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPNAIRealm;
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPRoamingConsortium;
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.ANQPVenueName;
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSConnCapability;
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSFriendlyName;
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSOSUProviders;
+import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSWANMetrics;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.wifi.supplicant.V1_0.ISupplicant;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback.BssidChangeReason;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
+import android.hardware.wifi.supplicant.V1_0.IfaceType;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
+import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.net.IpConfiguration;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.HwRemoteBinder;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import com.android.server.wifi.hotspot2.AnqpEvent;
+import com.android.server.wifi.hotspot2.IconEvent;
+import com.android.server.wifi.hotspot2.WnmData;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.ANQPParser;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+import com.android.server.wifi.util.NativeUtil;
+
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Hal calls for bring up/shut down of the supplicant daemon and for
+ * sending requests to the supplicant daemon
+ */
+public class SupplicantStaIfaceHal {
+ private static final String TAG = "SupplicantStaIfaceHal";
+ /**
+ * Regex pattern for extracting the wps device type bytes.
+ * Matches a strings like the following: "<categ>-<OUI>-<subcateg>";
+ */
+ private static final Pattern WPS_DEVICE_TYPE_PATTERN =
+ Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$");
+
+ private final Object mLock = new Object();
+ private boolean mVerboseLoggingEnabled = false;
+
+ // Supplicant HAL interface objects
+ private IServiceManager mIServiceManager = null;
+ private ISupplicant mISupplicant;
+ private ISupplicantStaIface mISupplicantStaIface;
+ private ISupplicantStaIfaceCallback mISupplicantStaIfaceCallback;
+ private final IServiceNotification mServiceNotificationCallback =
+ new IServiceNotification.Stub() {
+ public void onRegistration(String fqName, String name, boolean preexisting) {
+ synchronized (mLock) {
+ if (mVerboseLoggingEnabled) {
+ Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
+ + ", " + name + " preexisting=" + preexisting);
+ }
+ if (!initSupplicantService() || !initSupplicantStaIface()) {
+ Log.e(TAG, "initalizing ISupplicantIfaces failed.");
+ supplicantServiceDiedHandler();
+ } else {
+ Log.i(TAG, "Completed initialization of ISupplicant interfaces.");
+ }
+ }
+ }
+ };
+ private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
+ cookie -> {
+ Log.w(TAG, "IServiceManager died: cookie=" + cookie);
+ synchronized (mLock) {
+ supplicantServiceDiedHandler();
+ mIServiceManager = null; // Will need to register a new ServiceNotification
+ }
+ };
+ private final HwRemoteBinder.DeathRecipient mSupplicantDeathRecipient =
+ cookie -> {
+ Log.w(TAG, "ISupplicant/ISupplicantStaIface died: cookie=" + cookie);
+ synchronized (mLock) {
+ supplicantServiceDiedHandler();
+ }
+ };
+
+ private String mIfaceName;
+ private SupplicantStaNetworkHal mCurrentNetworkRemoteHandle;
+ private WifiConfiguration mCurrentNetworkLocalConfig;
+ private final Context mContext;
+ private final WifiMonitor mWifiMonitor;
+
+ public SupplicantStaIfaceHal(Context context, WifiMonitor monitor) {
+ mContext = context;
+ mWifiMonitor = monitor;
+ mISupplicantStaIfaceCallback = new SupplicantStaIfaceHalCallback();
+ }
+
+ /**
+ * Enable/Disable verbose logging.
+ *
+ * @param enable true to enable, false to disable.
+ */
+ void enableVerboseLogging(boolean enable) {
+ mVerboseLoggingEnabled = enable;
+ }
+
+ private boolean linkToServiceManagerDeath() {
+ if (mIServiceManager == null) return false;
+ try {
+ if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
+ supplicantServiceDiedHandler();
+ mIServiceManager = null; // Will need to register a new ServiceNotification
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "IServiceManager.linkToDeath exception", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Registers a service notification for the ISupplicant service, which triggers intialization of
+ * the ISupplicantStaIface
+ * @return true if the service notification was successfully registered
+ */
+ public boolean initialize() {
+ if (mVerboseLoggingEnabled) Log.i(TAG, "Registering ISupplicant service ready callback.");
+ synchronized (mLock) {
+ mISupplicant = null;
+ mISupplicantStaIface = null;
+ if (mIServiceManager != null) {
+ // Already have an IServiceManager and serviceNotification registered, don't
+ // don't register another.
+ return true;
+ }
+ try {
+ mIServiceManager = getServiceManagerMockable();
+ if (mIServiceManager == null) {
+ Log.e(TAG, "Failed to get HIDL Service Manager");
+ return false;
+ }
+ if (!linkToServiceManagerDeath()) {
+ return false;
+ }
+ /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it
+ exists */
+ if (!mIServiceManager.registerForNotifications(
+ ISupplicant.kInterfaceName, "", mServiceNotificationCallback)) {
+ Log.e(TAG, "Failed to register for notifications to "
+ + ISupplicant.kInterfaceName);
+ mIServiceManager = null; // Will need to register a new ServiceNotification
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: "
+ + e);
+ supplicantServiceDiedHandler();
+ }
+ return true;
+ }
+ }
+
+ private boolean linkToSupplicantDeath() {
+ if (mISupplicant == null) return false;
+ try {
+ if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
+ supplicantServiceDiedHandler();
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicant.linkToDeath exception", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean initSupplicantService() {
+ synchronized (mLock) {
+ try {
+ mISupplicant = getSupplicantMockable();
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicant.getService exception: " + e);
+ return false;
+ }
+ if (mISupplicant == null) {
+ Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
+ return false;
+ }
+ if (!linkToSupplicantDeath()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean linkToSupplicantStaIfaceDeath() {
+ if (mISupplicantStaIface == null) return false;
+ try {
+ if (!mISupplicantStaIface.linkToDeath(mSupplicantDeathRecipient, 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on ISupplicantStaIface");
+ supplicantServiceDiedHandler();
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantStaIface.linkToDeath exception", e);
+ return false;
+ }
+ return true;
+ }
+
+ private int getCurrentNetworkId() {
+ if (mCurrentNetworkLocalConfig == null) {
+ return WifiConfiguration.INVALID_NETWORK_ID;
+ }
+ return mCurrentNetworkLocalConfig.networkId;
+ }
+
+ private boolean initSupplicantStaIface() {
+ synchronized (mLock) {
+ /** List all supplicant Ifaces */
+ final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList<>();
+ try {
+ mISupplicant.listInterfaces((SupplicantStatus status,
+ ArrayList<ISupplicant.IfaceInfo> ifaces) -> {
+ if (status.code != SupplicantStatusCode.SUCCESS) {
+ Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code);
+ return;
+ }
+ supplicantIfaces.addAll(ifaces);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicant.listInterfaces exception: " + e);
+ return false;
+ }
+ if (supplicantIfaces.size() == 0) {
+ Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup.");
+ return false;
+ }
+ Mutable<ISupplicantIface> supplicantIface = new Mutable<>();
+ Mutable<String> ifaceName = new Mutable<>();
+ for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
+ if (ifaceInfo.type == IfaceType.STA) {
+ try {
+ mISupplicant.getInterface(ifaceInfo,
+ (SupplicantStatus status, ISupplicantIface iface) -> {
+ if (status.code != SupplicantStatusCode.SUCCESS) {
+ Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
+ return;
+ }
+ supplicantIface.value = iface;
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicant.getInterface exception: " + e);
+ return false;
+ }
+ ifaceName.value = ifaceInfo.name;
+ break;
+ }
+ }
+ if (supplicantIface.value == null) {
+ Log.e(TAG, "initSupplicantStaIface got null iface");
+ return false;
+ }
+ mISupplicantStaIface = getStaIfaceMockable(supplicantIface.value);
+ mIfaceName = ifaceName.value;
+ if (!linkToSupplicantStaIfaceDeath()) {
+ return false;
+ }
+ if (!registerCallback(mISupplicantStaIfaceCallback)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private void supplicantServiceDiedHandler() {
+ synchronized (mLock) {
+ mISupplicant = null;
+ mISupplicantStaIface = null;
+ mWifiMonitor.broadcastSupplicantDisconnectionEvent(mIfaceName);
+ }
+ }
+
+ /**
+ * Signals whether Initialization completed successfully.
+ */
+ public boolean isInitializationStarted() {
+ return mIServiceManager != null;
+ }
+
+ /**
+ * Signals whether Initialization completed successfully.
+ */
+ public boolean isInitializationComplete() {
+ return mISupplicantStaIface != null;
+ }
+
+ /**
+ * Wrapper functions to access static HAL methods, created to be mockable in unit tests
+ */
+ protected IServiceManager getServiceManagerMockable() throws RemoteException {
+ return IServiceManager.getService();
+ }
+
+ protected ISupplicant getSupplicantMockable() throws RemoteException {
+ return ISupplicant.getService();
+ }
+
+ protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
+ return ISupplicantStaIface.asInterface(iface.asBinder());
+ }
+
+ /**
+ * Add a network configuration to wpa_supplicant.
+ *
+ * @param config Config corresponding to the network.
+ * @return a Pair object including SupplicantStaNetworkHal and WifiConfiguration objects
+ * for the current network.
+ */
+ private Pair<SupplicantStaNetworkHal, WifiConfiguration>
+ addNetworkAndSaveConfig(WifiConfiguration config) {
+ logi("addSupplicantStaNetwork via HIDL");
+ if (config == null) {
+ loge("Cannot add NULL network!");
+ return null;
+ }
+ SupplicantStaNetworkHal network = addNetwork();
+ if (network == null) {
+ loge("Failed to add a network!");
+ return null;
+ }
+ boolean saveSuccess = false;
+ try {
+ saveSuccess = network.saveWifiConfiguration(config);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Exception while saving config params: " + config, e);
+ }
+ if (!saveSuccess) {
+ loge("Failed to save variables for: " + config.configKey());
+ if (!removeAllNetworks()) {
+ loge("Failed to remove all networks on failure.");
+ }
+ return null;
+ }
+ return new Pair(network, new WifiConfiguration(config));
+ }
+
+ /**
+ * Add the provided network configuration to wpa_supplicant and initiate connection to it.
+ * This method does the following:
+ * 1. If |config| is different to the current supplicant network, removes all supplicant
+ * networks and saves |config|.
+ * 2. Select the new network in wpa_supplicant.
+ *
+ * @param config WifiConfiguration parameters for the provided network.
+ * @return {@code true} if it succeeds, {@code false} otherwise
+ */
+ public boolean connectToNetwork(@NonNull WifiConfiguration config) {
+ logd("connectToNetwork " + config.configKey());
+ if (WifiConfigurationUtil.isSameNetwork(config, mCurrentNetworkLocalConfig)) {
+ logd("Network is already saved, will not trigger remove and add operation.");
+ } else {
+ mCurrentNetworkRemoteHandle = null;
+ mCurrentNetworkLocalConfig = null;
+ if (!removeAllNetworks()) {
+ loge("Failed to remove existing networks");
+ return false;
+ }
+ Pair<SupplicantStaNetworkHal, WifiConfiguration> pair = addNetworkAndSaveConfig(config);
+ if (pair == null) {
+ loge("Failed to add/save network configuration: " + config.configKey());
+ return false;
+ }
+ mCurrentNetworkRemoteHandle = pair.first;
+ mCurrentNetworkLocalConfig = pair.second;
+ }
+
+ if (!mCurrentNetworkRemoteHandle.select()) {
+ loge("Failed to select network configuration: " + config.configKey());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Initiates roaming to the already configured network in wpa_supplicant. If the network
+ * configuration provided does not match the already configured network, then this triggers
+ * a new connection attempt (instead of roam).
+ * 1. First check if we're attempting to connect to the same network as we currently have
+ * configured.
+ * 2. Set the new bssid for the network in wpa_supplicant.
+ * 3. Trigger reassociate command to wpa_supplicant.
+ *
+ * @param config WifiConfiguration parameters for the provided network.
+ * @return {@code true} if it succeeds, {@code false} otherwise
+ */
+ public boolean roamToNetwork(WifiConfiguration config) {
+ if (getCurrentNetworkId() != config.networkId) {
+ Log.w(TAG, "Cannot roam to a different network, initiate new connection. "
+ + "Current network ID: " + getCurrentNetworkId());
+ return connectToNetwork(config);
+ }
+ String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
+ logd("roamToNetwork" + config.configKey() + " (bssid " + bssid + ")");
+ if (!mCurrentNetworkRemoteHandle.setBssid(bssid)) {
+ loge("Failed to set new bssid on network: " + config.configKey());
+ return false;
+ }
+ if (!reassociate()) {
+ loge("Failed to trigger reassociate");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Load all the configured networks from wpa_supplicant.
+ *
+ * @param configs Map of configuration key to configuration objects corresponding to all
+ * the networks.
+ * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean loadNetworks(Map<String, WifiConfiguration> configs,
+ SparseArray<Map<String, String>> networkExtras) {
+ List<Integer> networkIds = listNetworks();
+ if (networkIds == null) {
+ Log.e(TAG, "Failed to list networks");
+ return false;
+ }
+ for (Integer networkId : networkIds) {
+ SupplicantStaNetworkHal network = getNetwork(networkId);
+ if (network == null) {
+ Log.e(TAG, "Failed to get network with ID: " + networkId);
+ return false;
+ }
+ WifiConfiguration config = new WifiConfiguration();
+ Map<String, String> networkExtra = new HashMap<>();
+ boolean loadSuccess = false;
+ try {
+ loadSuccess = network.loadWifiConfiguration(config, networkExtra);
+ } catch (IllegalArgumentException e) {
+ Log.wtf(TAG, "Exception while loading config params: " + config, e);
+ }
+ if (!loadSuccess) {
+ Log.e(TAG, "Failed to load wifi configuration for network with ID: " + networkId
+ + ". Skipping...");
+ continue;
+ }
+ // Set the default IP assignments.
+ config.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
+ config.setProxySettings(IpConfiguration.ProxySettings.NONE);
+
+ networkExtras.put(networkId, networkExtra);
+ String configKey = networkExtra.get(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY);
+ final WifiConfiguration duplicateConfig = configs.put(configKey, config);
+ if (duplicateConfig != null) {
+ // The network is already known. Overwrite the duplicate entry.
+ Log.i(TAG, "Replacing duplicate network: " + duplicateConfig.networkId);
+ removeNetwork(duplicateConfig.networkId);
+ networkExtras.remove(duplicateConfig.networkId);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Remove the request |networkId| from supplicant if it's the current network,
+ * if the current configured network matches |networkId|.
+ *
+ * @param networkId network id of the network to be removed from supplicant.
+ */
+ public void removeNetworkIfCurrent(int networkId) {
+ synchronized (mLock) {
+ if (getCurrentNetworkId() == networkId) {
+ // Currently we only save 1 network in supplicant.
+ removeAllNetworks();
+ }
+ }
+ }
+
+ /**
+ * Remove all networks from supplicant
+ */
+ public boolean removeAllNetworks() {
+ synchronized (mLock) {
+ ArrayList<Integer> networks = listNetworks();
+ if (networks == null) {
+ Log.e(TAG, "removeAllNetworks failed, got null networks");
+ return false;
+ }
+ for (int id : networks) {
+ if (!removeNetwork(id)) {
+ Log.e(TAG, "removeAllNetworks failed to remove network: " + id);
+ return false;
+ }
+ }
+ }
+ // Reset current network info. Probably not needed once we add support to remove/reset
+ // current network on receiving disconnection event from supplicant (b/32898136).
+ mCurrentNetworkLocalConfig = null;
+ mCurrentNetworkRemoteHandle = null;
+ return true;
+ }
+
+ /**
+ * Set the currently configured network's bssid.
+ *
+ * @param bssidStr Bssid to set in the form of "XX:XX:XX:XX:XX:XX"
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean setCurrentNetworkBssid(String bssidStr) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.setBssid(bssidStr);
+ }
+
+ /**
+ * Get the currently configured network's WPS NFC token.
+ *
+ * @return Hex string corresponding to the WPS NFC token.
+ */
+ public String getCurrentNetworkWpsNfcConfigurationToken() {
+ if (mCurrentNetworkRemoteHandle == null) return null;
+ return mCurrentNetworkRemoteHandle.getWpsNfcConfigurationToken();
+ }
+
+ /**
+ * Get the eap anonymous identity for the currently configured network.
+ *
+ * @return anonymous identity string if succeeds, null otherwise.
+ */
+ public String getCurrentNetworkEapAnonymousIdentity() {
+ if (mCurrentNetworkRemoteHandle == null) return null;
+ return mCurrentNetworkRemoteHandle.fetchEapAnonymousIdentity();
+ }
+
+ /**
+ * Send the eap identity response for the currently configured network.
+ *
+ * @param identityStr String to send.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendCurrentNetworkEapIdentityResponse(String identityStr) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapIdentityResponse(identityStr);
+ }
+
+ /**
+ * Send the eap sim gsm auth response for the currently configured network.
+ *
+ * @param paramsStr String to send.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendCurrentNetworkEapSimGsmAuthResponse(String paramsStr) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthResponse(paramsStr);
+ }
+
+ /**
+ * Send the eap sim gsm auth failure for the currently configured network.
+ *
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendCurrentNetworkEapSimGsmAuthFailure() {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimGsmAuthFailure();
+ }
+
+ /**
+ * Send the eap sim umts auth response for the currently configured network.
+ *
+ * @param paramsStr String to send.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendCurrentNetworkEapSimUmtsAuthResponse(String paramsStr) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthResponse(paramsStr);
+ }
+
+ /**
+ * Send the eap sim umts auts response for the currently configured network.
+ *
+ * @param paramsStr String to send.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendCurrentNetworkEapSimUmtsAutsResponse(String paramsStr) {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAutsResponse(paramsStr);
+ }
+
+ /**
+ * Send the eap sim umts auth failure for the currently configured network.
+ *
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendCurrentNetworkEapSimUmtsAuthFailure() {
+ if (mCurrentNetworkRemoteHandle == null) return false;
+ return mCurrentNetworkRemoteHandle.sendNetworkEapSimUmtsAuthFailure();
+ }
+
+ /**
+ * Adds a new network.
+ *
+ * @return The ISupplicantNetwork object for the new network, or null if the call fails
+ */
+ private SupplicantStaNetworkHal addNetwork() {
+ synchronized (mLock) {
+ final String methodStr = "addNetwork";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
+ Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
+ try {
+ mISupplicantStaIface.addNetwork((SupplicantStatus status,
+ ISupplicantNetwork network) -> {
+ if (checkStatusAndLogFailure(status, methodStr)) {
+ newNetwork.value = network;
+ }
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ }
+ if (newNetwork.value != null) {
+ return getStaNetworkMockable(
+ ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Remove network from supplicant with network Id
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ private boolean removeNetwork(int id) {
+ synchronized (mLock) {
+ final String methodStr = "removeNetwork";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.removeNetwork(id);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Use this to mock the creation of SupplicantStaNetworkHal instance.
+ *
+ * @param iSupplicantStaNetwork ISupplicantStaNetwork instance retrieved from HIDL.
+ * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
+ * the call fails
+ */
+ protected SupplicantStaNetworkHal getStaNetworkMockable(
+ ISupplicantStaNetwork iSupplicantStaNetwork) {
+ SupplicantStaNetworkHal network =
+ new SupplicantStaNetworkHal(iSupplicantStaNetwork, mIfaceName, mContext,
+ mWifiMonitor);
+ if (network != null) {
+ network.enableVerboseLogging(mVerboseLoggingEnabled);
+ }
+ return network;
+ }
+
+ /**
+ * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if
+ * the call fails
+ */
+ private SupplicantStaNetworkHal getNetwork(int id) {
+ synchronized (mLock) {
+ final String methodStr = "getNetwork";
+ Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>();
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
+ try {
+ mISupplicantStaIface.getNetwork(id, (SupplicantStatus status,
+ ISupplicantNetwork network) -> {
+ if (checkStatusAndLogFailure(status, methodStr)) {
+ gotNetwork.value = network;
+ }
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ }
+ if (gotNetwork.value != null) {
+ return getStaNetworkMockable(
+ ISupplicantStaNetwork.asInterface(gotNetwork.value.asBinder()));
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean registerCallback(ISupplicantStaIfaceCallback callback) {
+ synchronized (mLock) {
+ final String methodStr = "registerCallback";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.registerCallback(callback);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns
+ * null if the call fails
+ */
+ private java.util.ArrayList<Integer> listNetworks() {
+ synchronized (mLock) {
+ final String methodStr = "listNetworks";
+ Mutable<ArrayList<Integer>> networkIdList = new Mutable<>();
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
+ try {
+ mISupplicantStaIface.listNetworks((SupplicantStatus status,
+ java.util.ArrayList<Integer> networkIds) -> {
+ if (checkStatusAndLogFailure(status, methodStr)) {
+ networkIdList.value = networkIds;
+ }
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ }
+ return networkIdList.value;
+ }
+ }
+
+ /**
+ * Set WPS device name.
+ *
+ * @param name String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsDeviceName(String name) {
+ synchronized (mLock) {
+ final String methodStr = "setWpsDeviceName";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setWpsDeviceName(name);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set WPS device type.
+ *
+ * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsDeviceType(String typeStr) {
+ try {
+ Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
+ if (!match.find() || match.groupCount() != 3) {
+ Log.e(TAG, "Malformed WPS device type " + typeStr);
+ return false;
+ }
+ short categ = Short.parseShort(match.group(1));
+ byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
+ short subCateg = Short.parseShort(match.group(3));
+
+ byte[] bytes = new byte[8];
+ ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
+ byteBuffer.putShort(categ);
+ byteBuffer.put(oui);
+ byteBuffer.putShort(subCateg);
+ return setWpsDeviceType(bytes);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + typeStr, e);
+ return false;
+ }
+ }
+
+ private boolean setWpsDeviceType(byte[/* 8 */] type) {
+ synchronized (mLock) {
+ final String methodStr = "setWpsDeviceType";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setWpsDeviceType(type);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set WPS manufacturer.
+ *
+ * @param manufacturer String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsManufacturer(String manufacturer) {
+ synchronized (mLock) {
+ final String methodStr = "setWpsManufacturer";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setWpsManufacturer(manufacturer);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set WPS model name.
+ *
+ * @param modelName String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsModelName(String modelName) {
+ synchronized (mLock) {
+ final String methodStr = "setWpsModelName";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setWpsModelName(modelName);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set WPS model number.
+ *
+ * @param modelNumber String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsModelNumber(String modelNumber) {
+ synchronized (mLock) {
+ final String methodStr = "setWpsModelNumber";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setWpsModelNumber(modelNumber);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set WPS serial number.
+ *
+ * @param serialNumber String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsSerialNumber(String serialNumber) {
+ synchronized (mLock) {
+ final String methodStr = "setWpsSerialNumber";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setWpsSerialNumber(serialNumber);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set WPS config methods
+ *
+ * @param configMethodsStr List of config methods.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsConfigMethods(String configMethodsStr) {
+ short configMethodsMask = 0;
+ String[] configMethodsStrArr = configMethodsStr.split("\\s+");
+ for (int i = 0; i < configMethodsStrArr.length; i++) {
+ configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
+ }
+ return setWpsConfigMethods(configMethodsMask);
+ }
+
+ private boolean setWpsConfigMethods(short configMethods) {
+ synchronized (mLock) {
+ final String methodStr = "setWpsConfigMethods";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setWpsConfigMethods(configMethods);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Trigger a reassociation even if the iface is currently connected.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean reassociate() {
+ synchronized (mLock) {
+ final String methodStr = "reassociate";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.reassociate();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Trigger a reconnection if the iface is disconnected.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean reconnect() {
+ synchronized (mLock) {
+ final String methodStr = "reconnect";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.reconnect();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Trigger a disconnection from the currently connected network.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean disconnect() {
+ synchronized (mLock) {
+ final String methodStr = "disconnect";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.disconnect();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Enable or disable power save mode.
+ *
+ * @param enable true to enable, false to disable.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setPowerSave(boolean enable) {
+ synchronized (mLock) {
+ final String methodStr = "setPowerSave";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setPowerSave(enable);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Initiate TDLS discover with the specified AP.
+ *
+ * @param macAddress MAC Address of the AP.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean initiateTdlsDiscover(String macAddress) {
+ try {
+ return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + macAddress, e);
+ return false;
+ }
+ }
+ /** See ISupplicantStaIface.hal for documentation */
+ private boolean initiateTdlsDiscover(byte[/* 6 */] macAddress) {
+ synchronized (mLock) {
+ final String methodStr = "initiateTdlsDiscover";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.initiateTdlsDiscover(macAddress);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Initiate TDLS setup with the specified AP.
+ *
+ * @param macAddress MAC Address of the AP.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean initiateTdlsSetup(String macAddress) {
+ try {
+ return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + macAddress, e);
+ return false;
+ }
+ }
+ /** See ISupplicantStaIface.hal for documentation */
+ private boolean initiateTdlsSetup(byte[/* 6 */] macAddress) {
+ synchronized (mLock) {
+ final String methodStr = "initiateTdlsSetup";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.initiateTdlsSetup(macAddress);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Initiate TDLS teardown with the specified AP.
+ * @param macAddress MAC Address of the AP.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean initiateTdlsTeardown(String macAddress) {
+ try {
+ return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + macAddress, e);
+ return false;
+ }
+ }
+
+ /** See ISupplicantStaIface.hal for documentation */
+ private boolean initiateTdlsTeardown(byte[/* 6 */] macAddress) {
+ synchronized (mLock) {
+ final String methodStr = "initiateTdlsTeardown";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.initiateTdlsTeardown(macAddress);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Request the specified ANQP elements |elements| from the specified AP |bssid|.
+ *
+ * @param bssid BSSID of the AP
+ * @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId.
+ * @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean initiateAnqpQuery(String bssid, ArrayList<Short> infoElements,
+ ArrayList<Integer> hs20SubTypes) {
+ try {
+ return initiateAnqpQuery(
+ NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssid, e);
+ return false;
+ }
+ }
+
+ /** See ISupplicantStaIface.hal for documentation */
+ private boolean initiateAnqpQuery(byte[/* 6 */] macAddress,
+ java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) {
+ synchronized (mLock) {
+ final String methodStr = "initiateAnqpQuery";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.initiateAnqpQuery(macAddress,
+ infoElements, subTypes);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Request the specified ANQP ICON from the specified AP |bssid|.
+ *
+ * @param bssid BSSID of the AP
+ * @param fileName Name of the file to request.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean initiateHs20IconQuery(String bssid, String fileName) {
+ try {
+ return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssid, e);
+ return false;
+ }
+ }
+
+ /** See ISupplicantStaIface.hal for documentation */
+ private boolean initiateHs20IconQuery(byte[/* 6 */] macAddress, String fileName) {
+ synchronized (mLock) {
+ final String methodStr = "initiateHs20IconQuery";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.initiateHs20IconQuery(macAddress,
+ fileName);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Makes a callback to HIDL to getMacAddress from supplicant
+ *
+ * @return string containing the MAC address, or null on a failed call
+ */
+ public String getMacAddress() {
+ synchronized (mLock) {
+ final String methodStr = "getMacAddress";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
+ Mutable<String> gotMac = new Mutable<>();
+ try {
+ mISupplicantStaIface.getMacAddress((SupplicantStatus status,
+ byte[/* 6 */] macAddr) -> {
+ if (checkStatusAndLogFailure(status, methodStr)) {
+ gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
+ }
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ }
+ return gotMac.value;
+ }
+ }
+
+ /**
+ * Start using the added RX filters.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean startRxFilter() {
+ synchronized (mLock) {
+ final String methodStr = "startRxFilter";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.startRxFilter();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Stop using the added RX filters.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean stopRxFilter() {
+ synchronized (mLock) {
+ final String methodStr = "stopRxFilter";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.stopRxFilter();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Add an RX filter.
+ *
+ * @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
+ * {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean addRxFilter(int type) {
+ byte halType;
+ switch (type) {
+ case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
+ halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
+ break;
+ case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
+ halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
+ break;
+ default:
+ Log.e(TAG, "Invalid Rx Filter type: " + type);
+ return false;
+ }
+ return addRxFilter(halType);
+ }
+
+ public boolean addRxFilter(byte type) {
+ synchronized (mLock) {
+ final String methodStr = "addRxFilter";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.addRxFilter(type);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Remove an RX filter.
+ *
+ * @param type one of {@link WifiNative#RX_FILTER_TYPE_V4_MULTICAST}
+ * {@link WifiNative#RX_FILTER_TYPE_V6_MULTICAST} values.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean removeRxFilter(int type) {
+ byte halType;
+ switch (type) {
+ case WifiNative.RX_FILTER_TYPE_V4_MULTICAST:
+ halType = ISupplicantStaIface.RxFilterType.V4_MULTICAST;
+ break;
+ case WifiNative.RX_FILTER_TYPE_V6_MULTICAST:
+ halType = ISupplicantStaIface.RxFilterType.V6_MULTICAST;
+ break;
+ default:
+ Log.e(TAG, "Invalid Rx Filter type: " + type);
+ return false;
+ }
+ return removeRxFilter(halType);
+ }
+
+ public boolean removeRxFilter(byte type) {
+ synchronized (mLock) {
+ final String methodStr = "removeRxFilter";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.removeRxFilter(type);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set Bt co existense mode.
+ *
+ * @param mode one of the above {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_DISABLED},
+ * {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_ENABLED} or
+ * {@link WifiNative#BLUETOOTH_COEXISTENCE_MODE_SENSE}.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setBtCoexistenceMode(int mode) {
+ byte halMode;
+ switch (mode) {
+ case WifiNative.BLUETOOTH_COEXISTENCE_MODE_ENABLED:
+ halMode = ISupplicantStaIface.BtCoexistenceMode.ENABLED;
+ break;
+ case WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED:
+ halMode = ISupplicantStaIface.BtCoexistenceMode.DISABLED;
+ break;
+ case WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE:
+ halMode = ISupplicantStaIface.BtCoexistenceMode.SENSE;
+ break;
+ default:
+ Log.e(TAG, "Invalid Bt Coex mode: " + mode);
+ return false;
+ }
+ return setBtCoexistenceMode(halMode);
+ }
+
+ private boolean setBtCoexistenceMode(byte mode) {
+ synchronized (mLock) {
+ final String methodStr = "setBtCoexistenceMode";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setBtCoexistenceMode(mode);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /** Enable or disable BT coexistence mode.
+ *
+ * @param enable true to enable, false to disable.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setBtCoexistenceScanModeEnabled(boolean enable) {
+ synchronized (mLock) {
+ final String methodStr = "setBtCoexistenceScanModeEnabled";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status =
+ mISupplicantStaIface.setBtCoexistenceScanModeEnabled(enable);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Enable or disable suspend mode optimizations.
+ *
+ * @param enable true to enable, false otherwise.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setSuspendModeEnabled(boolean enable) {
+ synchronized (mLock) {
+ final String methodStr = "setSuspendModeEnabled";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setSuspendModeEnabled(enable);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set country code.
+ *
+ * @param codeStr 2 byte ASCII string. For ex: US, CA.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setCountryCode(String codeStr) {
+ if (TextUtils.isEmpty(codeStr)) return false;
+ return setCountryCode(NativeUtil.stringToByteArray(codeStr));
+ }
+
+ /** See ISupplicantStaIface.hal for documentation */
+ private boolean setCountryCode(byte[/* 2 */] code) {
+ synchronized (mLock) {
+ final String methodStr = "setCountryCode";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setCountryCode(code);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Start WPS pin registrar operation with the specified peer and pin.
+ *
+ * @param bssidStr BSSID of the peer.
+ * @param pin Pin to be used.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean startWpsRegistrar(String bssidStr, String pin) {
+ if (TextUtils.isEmpty(bssidStr) || TextUtils.isEmpty(pin)) return false;
+ try {
+ return startWpsRegistrar(NativeUtil.macAddressToByteArray(bssidStr), pin);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssidStr, e);
+ return false;
+ }
+ }
+
+ /** See ISupplicantStaIface.hal for documentation */
+ private boolean startWpsRegistrar(byte[/* 6 */] bssid, String pin) {
+ synchronized (mLock) {
+ final String methodStr = "startWpsRegistrar";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.startWpsRegistrar(bssid, pin);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Start WPS pin display operation with the specified peer.
+ *
+ * @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean startWpsPbc(String bssidStr) {
+ try {
+ return startWpsPbc(NativeUtil.macAddressToByteArray(bssidStr));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssidStr, e);
+ return false;
+ }
+ }
+
+ /** See ISupplicantStaIface.hal for documentation */
+ private boolean startWpsPbc(byte[/* 6 */] bssid) {
+ synchronized (mLock) {
+ final String methodStr = "startWpsPbc";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.startWpsPbc(bssid);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Start WPS pin keypad operation with the specified pin.
+ *
+ * @param pin Pin to be used.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean startWpsPinKeypad(String pin) {
+ if (TextUtils.isEmpty(pin)) return false;
+ synchronized (mLock) {
+ final String methodStr = "startWpsPinKeypad";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.startWpsPinKeypad(pin);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Start WPS pin display operation with the specified peer.
+ *
+ * @param bssidStr BSSID of the peer. Use empty bssid to indicate wildcard.
+ * @return new pin generated on success, null otherwise.
+ */
+ public String startWpsPinDisplay(String bssidStr) {
+ try {
+ return startWpsPinDisplay(NativeUtil.macAddressToByteArray(bssidStr));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssidStr, e);
+ return null;
+ }
+ }
+
+ /** See ISupplicantStaIface.hal for documentation */
+ private String startWpsPinDisplay(byte[/* 6 */] bssid) {
+ synchronized (mLock) {
+ final String methodStr = "startWpsPinDisplay";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
+ final Mutable<String> gotPin = new Mutable<>();
+ try {
+ mISupplicantStaIface.startWpsPinDisplay(bssid,
+ (SupplicantStatus status, String pin) -> {
+ if (checkStatusAndLogFailure(status, methodStr)) {
+ gotPin.value = pin;
+ }
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ }
+ return gotPin.value;
+ }
+ }
+
+ /**
+ * Cancels any ongoing WPS requests.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean cancelWps() {
+ synchronized (mLock) {
+ final String methodStr = "cancelWps";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.cancelWps();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Sets whether to use external sim for SIM/USIM processing.
+ *
+ * @param useExternalSim true to enable, false otherwise.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setExternalSim(boolean useExternalSim) {
+ synchronized (mLock) {
+ final String methodStr = "setExternalSim";
+ if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.setExternalSim(useExternalSim);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /** See ISupplicant.hal for documentation */
+ public boolean enableAutoReconnect(boolean enable) {
+ synchronized (mLock) {
+ final String methodStr = "enableAutoReconnect";
+ if (!checkSupplicantAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaIface.enableAutoReconnect(enable);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set the debug log level for wpa_supplicant
+ *
+ * @param turnOnVerbose Whether to turn on verbose logging or not.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setLogLevel(boolean turnOnVerbose) {
+ int logLevel = turnOnVerbose
+ ? ISupplicant.DebugLevel.DEBUG
+ : ISupplicant.DebugLevel.INFO;
+ return setDebugParams(logLevel, false, false);
+ }
+
+ /** See ISupplicant.hal for documentation */
+ private boolean setDebugParams(int level, boolean showTimestamp, boolean showKeys) {
+ synchronized (mLock) {
+ final String methodStr = "setDebugParams";
+ if (!checkSupplicantAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status =
+ mISupplicant.setDebugParams(level, showTimestamp, showKeys);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set concurrency priority between P2P & STA operations.
+ *
+ * @param isStaHigherPriority Set to true to prefer STA over P2P during concurrency operations,
+ * false otherwise.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setConcurrencyPriority(boolean isStaHigherPriority) {
+ if (isStaHigherPriority) {
+ return setConcurrencyPriority(IfaceType.STA);
+ } else {
+ return setConcurrencyPriority(IfaceType.P2P);
+ }
+ }
+
+ /** See ISupplicant.hal for documentation */
+ private boolean setConcurrencyPriority(int type) {
+ synchronized (mLock) {
+ final String methodStr = "setConcurrencyPriority";
+ if (!checkSupplicantAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicant.setConcurrencyPriority(type);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Returns false if Supplicant is null, and logs failure to call methodStr
+ */
+ private boolean checkSupplicantAndLogFailure(final String methodStr) {
+ if (mISupplicant == null) {
+ Log.e(TAG, "Can't call " + methodStr + ", ISupplicant is null");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns false if SupplicantStaIface is null, and logs failure to call methodStr
+ */
+ private boolean checkSupplicantStaIfaceAndLogFailure(final String methodStr) {
+ if (mISupplicantStaIface == null) {
+ Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if provided status code is SUCCESS, logs debug message and returns false
+ * otherwise
+ */
+ private boolean checkStatusAndLogFailure(SupplicantStatus status,
+ final String methodStr) {
+ if (status.code != SupplicantStatusCode.SUCCESS) {
+ Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed: "
+ + supplicantStatusCodeToString(status.code) + ", " + status.debugMessage);
+ return false;
+ } else {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ISupplicantStaIface." + methodStr + " succeeded");
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Helper function to log callbacks.
+ */
+ private void logCallback(final String methodStr) {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ISupplicantStaIfaceCallback." + methodStr + " received");
+ }
+ }
+
+
+ private void handleRemoteException(RemoteException e, String methodStr) {
+ supplicantServiceDiedHandler();
+ Log.e(TAG, "ISupplicantStaIface." + methodStr + " failed with exception", e);
+ }
+
+ /**
+ * Converts SupplicantStatus code values to strings for debug logging
+ * TODO(b/34811152) Remove this, or make it more break resistance
+ */
+ public static String supplicantStatusCodeToString(int code) {
+ switch (code) {
+ case 0:
+ return "SUCCESS";
+ case 1:
+ return "FAILURE_UNKNOWN";
+ case 2:
+ return "FAILURE_ARGS_INVALID";
+ case 3:
+ return "FAILURE_IFACE_INVALID";
+ case 4:
+ return "FAILURE_IFACE_UNKNOWN";
+ case 5:
+ return "FAILURE_IFACE_EXISTS";
+ case 6:
+ return "FAILURE_IFACE_DISABLED";
+ case 7:
+ return "FAILURE_IFACE_NOT_DISCONNECTED";
+ case 8:
+ return "FAILURE_NETWORK_INVALID";
+ case 9:
+ return "FAILURE_NETWORK_UNKNOWN";
+ default:
+ return "??? UNKNOWN_CODE";
+ }
+ }
+
+
+ /**
+ * Converts the Wps config method string to the equivalent enum value.
+ */
+ private static short stringToWpsConfigMethod(String configMethod) {
+ switch (configMethod) {
+ case "usba":
+ return WpsConfigMethods.USBA;
+ case "ethernet":
+ return WpsConfigMethods.ETHERNET;
+ case "label":
+ return WpsConfigMethods.LABEL;
+ case "display":
+ return WpsConfigMethods.DISPLAY;
+ case "int_nfc_token":
+ return WpsConfigMethods.INT_NFC_TOKEN;
+ case "ext_nfc_token":
+ return WpsConfigMethods.EXT_NFC_TOKEN;
+ case "nfc_interface":
+ return WpsConfigMethods.NFC_INTERFACE;
+ case "push_button":
+ return WpsConfigMethods.PUSHBUTTON;
+ case "keypad":
+ return WpsConfigMethods.KEYPAD;
+ case "virtual_push_button":
+ return WpsConfigMethods.VIRT_PUSHBUTTON;
+ case "physical_push_button":
+ return WpsConfigMethods.PHY_PUSHBUTTON;
+ case "p2ps":
+ return WpsConfigMethods.P2PS;
+ case "virtual_display":
+ return WpsConfigMethods.VIRT_DISPLAY;
+ case "physical_display":
+ return WpsConfigMethods.PHY_DISPLAY;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid WPS config method: " + configMethod);
+ }
+ }
+
+ /**
+ * Converts the supplicant state received from HIDL to the equivalent framework state.
+ */
+ private static SupplicantState supplicantHidlStateToFrameworkState(int state) {
+ switch (state) {
+ case ISupplicantStaIfaceCallback.State.DISCONNECTED:
+ return SupplicantState.DISCONNECTED;
+ case ISupplicantStaIfaceCallback.State.IFACE_DISABLED:
+ return SupplicantState.INTERFACE_DISABLED;
+ case ISupplicantStaIfaceCallback.State.INACTIVE:
+ return SupplicantState.INACTIVE;
+ case ISupplicantStaIfaceCallback.State.SCANNING:
+ return SupplicantState.SCANNING;
+ case ISupplicantStaIfaceCallback.State.AUTHENTICATING:
+ return SupplicantState.AUTHENTICATING;
+ case ISupplicantStaIfaceCallback.State.ASSOCIATING:
+ return SupplicantState.ASSOCIATING;
+ case ISupplicantStaIfaceCallback.State.ASSOCIATED:
+ return SupplicantState.ASSOCIATED;
+ case ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE:
+ return SupplicantState.FOUR_WAY_HANDSHAKE;
+ case ISupplicantStaIfaceCallback.State.GROUP_HANDSHAKE:
+ return SupplicantState.GROUP_HANDSHAKE;
+ case ISupplicantStaIfaceCallback.State.COMPLETED:
+ return SupplicantState.COMPLETED;
+ default:
+ throw new IllegalArgumentException("Invalid state: " + state);
+ }
+ }
+
+ private static class Mutable<E> {
+ public E value;
+
+ Mutable() {
+ value = null;
+ }
+
+ Mutable(E value) {
+ this.value = value;
+ }
+ }
+
+ private class SupplicantStaIfaceHalCallback extends ISupplicantStaIfaceCallback.Stub {
+ private static final int WLAN_REASON_IE_IN_4WAY_DIFFERS = 17; // IEEE 802.11i
+ private boolean mStateIsFourway = false; // Used to help check for PSK password mismatch
+
+ /**
+ * Parses the provided payload into an ANQP element.
+ *
+ * @param infoID Element type.
+ * @param payload Raw payload bytes.
+ * @return AnqpElement instance on success, null on failure.
+ */
+ private ANQPElement parseAnqpElement(Constants.ANQPElementType infoID,
+ ArrayList<Byte> payload) {
+ try {
+ return Constants.getANQPElementID(infoID) != null
+ ? ANQPParser.parseElement(
+ infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)))
+ : ANQPParser.parseHS20Element(
+ infoID, ByteBuffer.wrap(NativeUtil.byteArrayFromArrayList(payload)));
+ } catch (IOException | BufferUnderflowException e) {
+ Log.e(TAG, "Failed parsing ANQP element payload: " + infoID, e);
+ return null;
+ }
+ }
+
+ /**
+ * Parse the ANQP element data and add to the provided elements map if successful.
+ *
+ * @param elementsMap Map to add the parsed out element to.
+ * @param infoID Element type.
+ * @param payload Raw payload bytes.
+ */
+ private void addAnqpElementToMap(Map<Constants.ANQPElementType, ANQPElement> elementsMap,
+ Constants.ANQPElementType infoID,
+ ArrayList<Byte> payload) {
+ if (payload == null || payload.isEmpty()) return;
+ ANQPElement element = parseAnqpElement(infoID, payload);
+ if (element != null) {
+ elementsMap.put(infoID, element);
+ }
+ }
+
+ @Override
+ public void onNetworkAdded(int id) {
+ logCallback("onNetworkAdded");
+ }
+
+ @Override
+ public void onNetworkRemoved(int id) {
+ logCallback("onNetworkRemoved");
+ }
+
+ @Override
+ public void onStateChanged(int newState, byte[/* 6 */] bssid, int id,
+ ArrayList<Byte> ssid) {
+ logCallback("onStateChanged");
+ synchronized (mLock) {
+ SupplicantState newSupplicantState = supplicantHidlStateToFrameworkState(newState);
+ WifiSsid wifiSsid =
+ WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(ssid));
+ String bssidStr = NativeUtil.macAddressFromByteArray(bssid);
+ mStateIsFourway = (newState == ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE);
+ if (newSupplicantState == SupplicantState.COMPLETED) {
+ mWifiMonitor.broadcastNetworkConnectionEvent(
+ mIfaceName, getCurrentNetworkId(), bssidStr);
+ }
+ mWifiMonitor.broadcastSupplicantStateChangeEvent(
+ mIfaceName, getCurrentNetworkId(), wifiSsid, bssidStr, newSupplicantState);
+ }
+ }
+
+ @Override
+ public void onAnqpQueryDone(byte[/* 6 */] bssid,
+ ISupplicantStaIfaceCallback.AnqpData data,
+ ISupplicantStaIfaceCallback.Hs20AnqpData hs20Data) {
+ logCallback("onAnqpQueryDone");
+ synchronized (mLock) {
+ Map<Constants.ANQPElementType, ANQPElement> elementsMap = new HashMap<>();
+ addAnqpElementToMap(elementsMap, ANQPVenueName, data.venueName);
+ addAnqpElementToMap(elementsMap, ANQPRoamingConsortium, data.roamingConsortium);
+ addAnqpElementToMap(
+ elementsMap, ANQPIPAddrAvailability, data.ipAddrTypeAvailability);
+ addAnqpElementToMap(elementsMap, ANQPNAIRealm, data.naiRealm);
+ addAnqpElementToMap(elementsMap, ANQP3GPPNetwork, data.anqp3gppCellularNetwork);
+ addAnqpElementToMap(elementsMap, ANQPDomName, data.domainName);
+ addAnqpElementToMap(elementsMap, HSFriendlyName, hs20Data.operatorFriendlyName);
+ addAnqpElementToMap(elementsMap, HSWANMetrics, hs20Data.wanMetrics);
+ addAnqpElementToMap(elementsMap, HSConnCapability, hs20Data.connectionCapability);
+ addAnqpElementToMap(elementsMap, HSOSUProviders, hs20Data.osuProvidersList);
+ mWifiMonitor.broadcastAnqpDoneEvent(
+ mIfaceName, new AnqpEvent(NativeUtil.macAddressToLong(bssid), elementsMap));
+ }
+ }
+
+ @Override
+ public void onHs20IconQueryDone(byte[/* 6 */] bssid, String fileName,
+ ArrayList<Byte> data) {
+ logCallback("onHs20IconQueryDone");
+ synchronized (mLock) {
+ mWifiMonitor.broadcastIconDoneEvent(
+ mIfaceName,
+ new IconEvent(NativeUtil.macAddressToLong(bssid), fileName, data.size(),
+ NativeUtil.byteArrayFromArrayList(data)));
+ }
+ }
+
+ @Override
+ public void onHs20SubscriptionRemediation(byte[/* 6 */] bssid, byte osuMethod, String url) {
+ logCallback("onHs20SubscriptionRemediation");
+ synchronized (mLock) {
+ mWifiMonitor.broadcastWnmEvent(
+ mIfaceName,
+ new WnmData(NativeUtil.macAddressToLong(bssid), url, osuMethod));
+ }
+ }
+
+ @Override
+ public void onHs20DeauthImminentNotice(byte[/* 6 */] bssid, int reasonCode,
+ int reAuthDelayInSec, String url) {
+ logCallback("onHs20DeauthImminentNotice");
+ synchronized (mLock) {
+ mWifiMonitor.broadcastWnmEvent(
+ mIfaceName,
+ new WnmData(NativeUtil.macAddressToLong(bssid), url,
+ reasonCode == WnmData.ESS, reAuthDelayInSec));
+ }
+ }
+
+ @Override
+ public void onDisconnected(byte[/* 6 */] bssid, boolean locallyGenerated, int reasonCode) {
+ logCallback("onDisconnected");
+ synchronized (mLock) {
+ if (mVerboseLoggingEnabled) {
+ Log.e(TAG, "onDisconnected 4way=" + mStateIsFourway
+ + " locallyGenerated=" + locallyGenerated
+ + " reasonCode=" + reasonCode);
+ }
+ if (mStateIsFourway
+ && (!locallyGenerated || reasonCode != WLAN_REASON_IE_IN_4WAY_DIFFERS)) {
+ mWifiMonitor.broadcastAuthenticationFailureEvent(
+ mIfaceName, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
+ }
+ mWifiMonitor.broadcastNetworkDisconnectionEvent(
+ mIfaceName, locallyGenerated ? 1 : 0, reasonCode,
+ NativeUtil.macAddressFromByteArray(bssid));
+ }
+ }
+
+ @Override
+ public void onAssociationRejected(byte[/* 6 */] bssid, int statusCode, boolean timedOut) {
+ logCallback("onAssociationRejected");
+ synchronized (mLock) {
+ mWifiMonitor.broadcastAssociationRejectionEvent(mIfaceName, statusCode, timedOut,
+ NativeUtil.macAddressFromByteArray(bssid));
+ }
+ }
+
+ @Override
+ public void onAuthenticationTimeout(byte[/* 6 */] bssid) {
+ logCallback("onAuthenticationTimeout");
+ synchronized (mLock) {
+ mWifiMonitor.broadcastAuthenticationFailureEvent(
+ mIfaceName, WifiManager.ERROR_AUTH_FAILURE_TIMEOUT);
+ }
+ }
+
+ @Override
+ public void onBssidChanged(byte reason, byte[/* 6 */] bssid) {
+ logCallback("onBssidChanged");
+ synchronized (mLock) {
+ if (reason == BssidChangeReason.ASSOC_START) {
+ mWifiMonitor.broadcastTargetBssidEvent(
+ mIfaceName, NativeUtil.macAddressFromByteArray(bssid));
+ } else if (reason == BssidChangeReason.ASSOC_COMPLETE) {
+ mWifiMonitor.broadcastAssociatedBssidEvent(
+ mIfaceName, NativeUtil.macAddressFromByteArray(bssid));
+ }
+ }
+ }
+
+ @Override
+ public void onEapFailure() {
+ logCallback("onEapFailure");
+ synchronized (mLock) {
+ mWifiMonitor.broadcastAuthenticationFailureEvent(
+ mIfaceName, WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE);
+ }
+ }
+
+ @Override
+ public void onWpsEventSuccess() {
+ logCallback("onWpsEventSuccess");
+ synchronized (mLock) {
+ mWifiMonitor.broadcastWpsSuccessEvent(mIfaceName);
+ }
+ }
+
+ @Override
+ public void onWpsEventFail(byte[/* 6 */] bssid, short configError, short errorInd) {
+ logCallback("onWpsEventFail");
+ synchronized (mLock) {
+ if (configError == WpsConfigError.MSG_TIMEOUT
+ && errorInd == WpsErrorIndication.NO_ERROR) {
+ mWifiMonitor.broadcastWpsTimeoutEvent(mIfaceName);
+ } else {
+ mWifiMonitor.broadcastWpsFailEvent(mIfaceName, configError, errorInd);
+ }
+ }
+ }
+
+ @Override
+ public void onWpsEventPbcOverlap() {
+ logCallback("onWpsEventPbcOverlap");
+ synchronized (mLock) {
+ mWifiMonitor.broadcastWpsOverlapEvent(mIfaceName);
+ }
+ }
+
+ @Override
+ public void onExtRadioWorkStart(int id) {
+ logCallback("onExtRadioWorkStart");
+ }
+
+ @Override
+ public void onExtRadioWorkTimeout(int id) {
+ logCallback("onExtRadioWorkTimeout");
+ }
+ }
+
+ private void logd(String s) {
+ Log.d(TAG, s);
+ }
+
+ private void logi(String s) {
+ Log.i(TAG, s);
+ }
+
+ private void loge(String s) {
+ Log.e(TAG, s);
+ }
+}
diff --git a/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
new file mode 100644
index 0000000..6e7d98c
--- /dev/null
+++ b/service/java/com/android/server/wifi/SupplicantStaNetworkHal.java
@@ -0,0 +1,2533 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wifi;
+
+import android.content.Context;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetworkCallback;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.MutableBoolean;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.wifi.util.NativeUtil;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+/**
+ * Wrapper class for ISupplicantStaNetwork HAL calls. Gets and sets supplicant sta network variables
+ * and interacts with networks.
+ * Public fields should be treated as invalid until their 'get' method is called, which will set the
+ * value if it returns true
+ */
+public class SupplicantStaNetworkHal {
+ private static final String TAG = "SupplicantStaNetworkHal";
+ @VisibleForTesting
+ public static final String ID_STRING_KEY_FQDN = "fqdn";
+ @VisibleForTesting
+ public static final String ID_STRING_KEY_CREATOR_UID = "creatorUid";
+ @VisibleForTesting
+ public static final String ID_STRING_KEY_CONFIG_KEY = "configKey";
+
+ /**
+ * Regex pattern for extracting the GSM sim authentication response params from a string.
+ * Matches a strings like the following: "[:<kc_value>:<sres_value>]";
+ */
+ private static final Pattern GSM_AUTH_RESPONSE_PARAMS_PATTERN =
+ Pattern.compile(":([0-9a-fA-F]+):([0-9a-fA-F]+)");
+ /**
+ * Regex pattern for extracting the UMTS sim authentication response params from a string.
+ * Matches a strings like the following: ":<ik_value>:<ck_value>:<res_value>";
+ */
+ private static final Pattern UMTS_AUTH_RESPONSE_PARAMS_PATTERN =
+ Pattern.compile("^:([0-9a-fA-F]+):([0-9a-fA-F]+):([0-9a-fA-F]+)$");
+ /**
+ * Regex pattern for extracting the UMTS sim auts response params from a string.
+ * Matches a strings like the following: ":<auts_value>";
+ */
+ private static final Pattern UMTS_AUTS_RESPONSE_PARAMS_PATTERN =
+ Pattern.compile("^:([0-9a-fA-F]+)$");
+
+ private final Object mLock = new Object();
+ private final String mIfaceName;
+ private final WifiMonitor mWifiMonitor;
+ private ISupplicantStaNetwork mISupplicantStaNetwork;
+ private ISupplicantStaNetworkCallback mISupplicantStaNetworkCallback;
+
+ private boolean mVerboseLoggingEnabled = false;
+ // Indicates whether the system is capable of 802.11r fast BSS transition.
+ private boolean mSystemSupportsFastBssTransition = false;
+
+ // Network variables read from wpa_supplicant.
+ private int mNetworkId;
+ private ArrayList<Byte> mSsid;
+ private byte[/* 6 */] mBssid;
+ private boolean mScanSsid;
+ private int mKeyMgmtMask;
+ private int mProtoMask;
+ private int mAuthAlgMask;
+ private int mGroupCipherMask;
+ private int mPairwiseCipherMask;
+ private String mPskPassphrase;
+ private byte[] mPsk;
+ private ArrayList<Byte> mWepKey;
+ private int mWepTxKeyIdx;
+ private boolean mRequirePmf;
+ private String mIdStr;
+ private int mEapMethod;
+ private int mEapPhase2Method;
+ private ArrayList<Byte> mEapIdentity;
+ private ArrayList<Byte> mEapAnonymousIdentity;
+ private ArrayList<Byte> mEapPassword;
+ private String mEapCACert;
+ private String mEapCAPath;
+ private String mEapClientCert;
+ private String mEapPrivateKeyId;
+ private String mEapSubjectMatch;
+ private String mEapAltSubjectMatch;
+ private boolean mEapEngine;
+ private String mEapEngineID;
+ private String mEapDomainSuffixMatch;
+
+ SupplicantStaNetworkHal(ISupplicantStaNetwork iSupplicantStaNetwork, String ifaceName,
+ Context context, WifiMonitor monitor) {
+ mISupplicantStaNetwork = iSupplicantStaNetwork;
+ mIfaceName = ifaceName;
+ mWifiMonitor = monitor;
+ mSystemSupportsFastBssTransition =
+ context.getResources().getBoolean(R.bool.config_wifi_fast_bss_transition_enabled);
+ }
+
+ /**
+ * Enable/Disable verbose logging.
+ *
+ * @param enable true to enable, false to disable.
+ */
+ void enableVerboseLogging(boolean enable) {
+ mVerboseLoggingEnabled = enable;
+ }
+
+ /**
+ * Read network variables from wpa_supplicant into the provided WifiConfiguration object.
+ *
+ * @param config WifiConfiguration object to be populated.
+ * @param networkExtras Map of network extras parsed from wpa_supplicant.
+ * @return true if succeeds, false otherwise.
+ * @throws IllegalArgumentException on malformed configuration params.
+ */
+ public boolean loadWifiConfiguration(WifiConfiguration config,
+ Map<String, String> networkExtras) {
+ if (config == null) return false;
+ /** SSID */
+ config.SSID = null;
+ if (getSsid() && !ArrayUtils.isEmpty(mSsid)) {
+ config.SSID = NativeUtil.encodeSsid(mSsid);
+ } else {
+ Log.e(TAG, "failed to read ssid");
+ return false;
+ }
+ /** Network Id */
+ config.networkId = -1;
+ if (getId()) {
+ config.networkId = mNetworkId;
+ } else {
+ Log.e(TAG, "getId failed");
+ return false;
+ }
+ /** BSSID */
+ config.getNetworkSelectionStatus().setNetworkSelectionBSSID(null);
+ if (getBssid() && !ArrayUtils.isEmpty(mBssid)) {
+ config.getNetworkSelectionStatus().setNetworkSelectionBSSID(
+ NativeUtil.macAddressFromByteArray(mBssid));
+ }
+ /** Scan SSID (Is Hidden Network?) */
+ config.hiddenSSID = false;
+ if (getScanSsid()) {
+ config.hiddenSSID = mScanSsid;
+ }
+ /** Require PMF*/
+ config.requirePMF = false;
+ if (getRequirePmf()) {
+ config.requirePMF = mRequirePmf;
+ }
+ /** WEP keys **/
+ config.wepTxKeyIndex = -1;
+ if (getWepTxKeyIdx()) {
+ config.wepTxKeyIndex = mWepTxKeyIdx;
+ }
+ for (int i = 0; i < 4; i++) {
+ config.wepKeys[i] = null;
+ if (getWepKey(i) && !ArrayUtils.isEmpty(mWepKey)) {
+ config.wepKeys[i] = NativeUtil.bytesToHexOrQuotedAsciiString(mWepKey);
+ }
+ }
+ /** PSK pass phrase */
+ config.preSharedKey = null;
+ if (getPskPassphrase() && !TextUtils.isEmpty(mPskPassphrase)) {
+ config.preSharedKey = NativeUtil.addEnclosingQuotes(mPskPassphrase);
+ } else if (getPsk() && !ArrayUtils.isEmpty(mPsk)) {
+ config.preSharedKey = NativeUtil.hexStringFromByteArray(mPsk);
+ }
+ /** allowedKeyManagement */
+ if (getKeyMgmt()) {
+ BitSet keyMgmtMask = supplicantToWifiConfigurationKeyMgmtMask(mKeyMgmtMask);
+ config.allowedKeyManagement = removeFastTransitionFlags(keyMgmtMask);
+ }
+ /** allowedProtocols */
+ if (getProto()) {
+ config.allowedProtocols =
+ supplicantToWifiConfigurationProtoMask(mProtoMask);
+ }
+ /** allowedAuthAlgorithms */
+ if (getAuthAlg()) {
+ config.allowedAuthAlgorithms =
+ supplicantToWifiConfigurationAuthAlgMask(mAuthAlgMask);
+ }
+ /** allowedGroupCiphers */
+ if (getGroupCipher()) {
+ config.allowedGroupCiphers =
+ supplicantToWifiConfigurationGroupCipherMask(mGroupCipherMask);
+ }
+ /** allowedPairwiseCiphers */
+ if (getPairwiseCipher()) {
+ config.allowedPairwiseCiphers =
+ supplicantToWifiConfigurationPairwiseCipherMask(mPairwiseCipherMask);
+ }
+ /** metadata: idstr */
+ if (getIdStr() && !TextUtils.isEmpty(mIdStr)) {
+ Map<String, String> metadata = parseNetworkExtra(mIdStr);
+ networkExtras.putAll(metadata);
+ } else {
+ Log.w(TAG, "getIdStr failed or empty");
+ }
+ return loadWifiEnterpriseConfig(config.SSID, config.enterpriseConfig);
+ }
+
+ /**
+ * Save an entire WifiConfiguration to wpa_supplicant via HIDL.
+ *
+ * @param config WifiConfiguration object to be saved.
+ * @return true if succeeds, false otherwise.
+ * @throws IllegalArgumentException on malformed configuration params.
+ */
+ public boolean saveWifiConfiguration(WifiConfiguration config) {
+ if (config == null) return false;
+ /** SSID */
+ if (config.SSID != null) {
+ if (!setSsid(NativeUtil.decodeSsid(config.SSID))) {
+ Log.e(TAG, "failed to set SSID: " + config.SSID);
+ return false;
+ }
+ }
+ /** BSSID */
+ String bssidStr = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
+ if (bssidStr != null) {
+ byte[] bssid = NativeUtil.macAddressToByteArray(bssidStr);
+ if (!setBssid(bssid)) {
+ Log.e(TAG, "failed to set BSSID: " + bssidStr);
+ return false;
+ }
+ }
+ /** Pre Shared Key. This can either be quoted ASCII passphrase or hex string for raw psk */
+ if (config.preSharedKey != null) {
+ if (config.preSharedKey.startsWith("\"")) {
+ if (!setPskPassphrase(NativeUtil.removeEnclosingQuotes(config.preSharedKey))) {
+ Log.e(TAG, "failed to set psk passphrase");
+ return false;
+ }
+ } else {
+ if (!setPsk(NativeUtil.hexStringToByteArray(config.preSharedKey))) {
+ Log.e(TAG, "failed to set psk");
+ return false;
+ }
+ }
+ }
+
+ /** Wep Keys */
+ boolean hasSetKey = false;
+ if (config.wepKeys != null) {
+ for (int i = 0; i < config.wepKeys.length; i++) {
+ if (config.wepKeys[i] != null) {
+ if (!setWepKey(
+ i, NativeUtil.hexOrQuotedAsciiStringToBytes(config.wepKeys[i]))) {
+ Log.e(TAG, "failed to set wep_key " + i);
+ return false;
+ }
+ hasSetKey = true;
+ }
+ }
+ }
+ /** Wep Tx Key Idx */
+ if (hasSetKey) {
+ if (!setWepTxKeyIdx(config.wepTxKeyIndex)) {
+ Log.e(TAG, "failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
+ return false;
+ }
+ }
+ /** HiddenSSID */
+ if (!setScanSsid(config.hiddenSSID)) {
+ Log.e(TAG, config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID);
+ return false;
+ }
+ /** RequirePMF */
+ if (!setRequirePmf(config.requirePMF)) {
+ Log.e(TAG, config.SSID + ": failed to set requirePMF: " + config.requirePMF);
+ return false;
+ }
+ /** Key Management Scheme */
+ if (config.allowedKeyManagement.cardinality() != 0) {
+ // Add FT flags if supported.
+ BitSet keyMgmtMask = addFastTransitionFlags(config.allowedKeyManagement);
+ if (!setKeyMgmt(wifiConfigurationToSupplicantKeyMgmtMask(keyMgmtMask))) {
+ Log.e(TAG, "failed to set Key Management");
+ return false;
+ }
+ }
+ /** Security Protocol */
+ if (config.allowedProtocols.cardinality() != 0
+ && !setProto(wifiConfigurationToSupplicantProtoMask(config.allowedProtocols))) {
+ Log.e(TAG, "failed to set Security Protocol");
+ return false;
+ }
+ /** Auth Algorithm */
+ if (config.allowedAuthAlgorithms.cardinality() != 0
+ && !setAuthAlg(wifiConfigurationToSupplicantAuthAlgMask(
+ config.allowedAuthAlgorithms))) {
+ Log.e(TAG, "failed to set AuthAlgorithm");
+ return false;
+ }
+ /** Group Cipher */
+ if (config.allowedGroupCiphers.cardinality() != 0
+ && !setGroupCipher(wifiConfigurationToSupplicantGroupCipherMask(
+ config.allowedGroupCiphers))) {
+ Log.e(TAG, "failed to set Group Cipher");
+ return false;
+ }
+ /** Pairwise Cipher*/
+ if (config.allowedPairwiseCiphers.cardinality() != 0
+ && !setPairwiseCipher(wifiConfigurationToSupplicantPairwiseCipherMask(
+ config.allowedPairwiseCiphers))) {
+ Log.e(TAG, "failed to set PairwiseCipher");
+ return false;
+ }
+ /** metadata: FQDN + ConfigKey + CreatorUid */
+ final Map<String, String> metadata = new HashMap<String, String>();
+ if (config.isPasspoint()) {
+ metadata.put(ID_STRING_KEY_FQDN, config.FQDN);
+ }
+ metadata.put(ID_STRING_KEY_CONFIG_KEY, config.configKey());
+ metadata.put(ID_STRING_KEY_CREATOR_UID, Integer.toString(config.creatorUid));
+ if (!setIdStr(createNetworkExtra(metadata))) {
+ Log.e(TAG, "failed to set id string");
+ return false;
+ }
+ /** UpdateIdentifier */
+ if (config.updateIdentifier != null
+ && !setUpdateIdentifier(Integer.parseInt(config.updateIdentifier))) {
+ Log.e(TAG, "failed to set update identifier");
+ return false;
+ }
+ // Finish here if no EAP config to set
+ if (config.enterpriseConfig != null
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
+ if (!saveWifiEnterpriseConfig(config.SSID, config.enterpriseConfig)) {
+ return false;
+ }
+ }
+
+ // Now that the network is configured fully, start listening for callback events.
+ mISupplicantStaNetworkCallback =
+ new SupplicantStaNetworkHalCallback(config.networkId, config.SSID);
+ if (!registerCallback(mISupplicantStaNetworkCallback)) {
+ Log.e(TAG, "Failed to register callback");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Read network variables from wpa_supplicant into the provided WifiEnterpriseConfig object.
+ *
+ * @param ssid SSID of the network. (Used for logging purposes only)
+ * @param eapConfig WifiEnterpriseConfig object to be populated.
+ * @return true if succeeds, false otherwise.
+ */
+ private boolean loadWifiEnterpriseConfig(String ssid, WifiEnterpriseConfig eapConfig) {
+ if (eapConfig == null) return false;
+ /** EAP method */
+ if (getEapMethod()) {
+ eapConfig.setEapMethod(supplicantToWifiConfigurationEapMethod(mEapMethod));
+ } else {
+ // Invalid eap method could be because it's not an enterprise config.
+ Log.e(TAG, "failed to get eap method. Assumimg not an enterprise network");
+ return true;
+ }
+ /** EAP Phase 2 method */
+ if (getEapPhase2Method()) {
+ eapConfig.setPhase2Method(
+ supplicantToWifiConfigurationEapPhase2Method(mEapPhase2Method));
+ } else {
+ // We cannot have an invalid eap phase 2 method. Return failure.
+ Log.e(TAG, "failed to get eap phase2 method");
+ return false;
+ }
+ /** EAP Identity */
+ if (getEapIdentity() && !ArrayUtils.isEmpty(mEapIdentity)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.IDENTITY_KEY,
+ NativeUtil.stringFromByteArrayList(mEapIdentity));
+ }
+ /** EAP Anonymous Identity */
+ if (getEapAnonymousIdentity() && !ArrayUtils.isEmpty(mEapAnonymousIdentity)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.ANON_IDENTITY_KEY,
+ NativeUtil.stringFromByteArrayList(mEapAnonymousIdentity));
+ }
+ /** EAP Password */
+ if (getEapPassword() && !ArrayUtils.isEmpty(mEapPassword)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.PASSWORD_KEY,
+ NativeUtil.stringFromByteArrayList(mEapPassword));
+ }
+ /** EAP Client Cert */
+ if (getEapClientCert() && !TextUtils.isEmpty(mEapClientCert)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY, mEapClientCert);
+ }
+ /** EAP CA Cert */
+ if (getEapCACert() && !TextUtils.isEmpty(mEapCACert)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, mEapCACert);
+ }
+ /** EAP Subject Match */
+ if (getEapSubjectMatch() && !TextUtils.isEmpty(mEapSubjectMatch)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY, mEapSubjectMatch);
+ }
+ /** EAP Engine ID */
+ if (getEapEngineID() && !TextUtils.isEmpty(mEapEngineID)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY, mEapEngineID);
+ }
+ /** EAP Engine. Set this only if the engine id is non null. */
+ if (getEapEngine() && !TextUtils.isEmpty(mEapEngineID)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.ENGINE_KEY,
+ mEapEngine
+ ? WifiEnterpriseConfig.ENGINE_ENABLE
+ : WifiEnterpriseConfig.ENGINE_DISABLE);
+ }
+ /** EAP Private Key */
+ if (getEapPrivateKeyId() && !TextUtils.isEmpty(mEapPrivateKeyId)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY, mEapPrivateKeyId);
+ }
+ /** EAP Alt Subject Match */
+ if (getEapAltSubjectMatch() && !TextUtils.isEmpty(mEapAltSubjectMatch)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY, mEapAltSubjectMatch);
+ }
+ /** EAP Domain Suffix Match */
+ if (getEapDomainSuffixMatch() && !TextUtils.isEmpty(mEapDomainSuffixMatch)) {
+ eapConfig.setFieldValue(
+ WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY, mEapDomainSuffixMatch);
+ }
+ /** EAP CA Path*/
+ if (getEapCAPath() && !TextUtils.isEmpty(mEapCAPath)) {
+ eapConfig.setFieldValue(WifiEnterpriseConfig.CA_PATH_KEY, mEapCAPath);
+ }
+ return true;
+ }
+
+ /**
+ * Save network variables from the provided WifiEnterpriseConfig object to wpa_supplicant.
+ *
+ * @param ssid SSID of the network. (Used for logging purposes only)
+ * @param eapConfig WifiEnterpriseConfig object to be saved.
+ * @return true if succeeds, false otherwise.
+ */
+ private boolean saveWifiEnterpriseConfig(String ssid, WifiEnterpriseConfig eapConfig) {
+ if (eapConfig == null) return false;
+ /** EAP method */
+ if (!setEapMethod(wifiConfigurationToSupplicantEapMethod(eapConfig.getEapMethod()))) {
+ Log.e(TAG, ssid + ": failed to set eap method: " + eapConfig.getEapMethod());
+ return false;
+ }
+ /** EAP Phase 2 method */
+ if (!setEapPhase2Method(wifiConfigurationToSupplicantEapPhase2Method(
+ eapConfig.getPhase2Method()))) {
+ Log.e(TAG, ssid + ": failed to set eap phase 2 method: " + eapConfig.getPhase2Method());
+ return false;
+ }
+ String eapParam = null;
+ /** EAP Identity */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.IDENTITY_KEY);
+ if (!TextUtils.isEmpty(eapParam)
+ && !setEapIdentity(NativeUtil.stringToByteArrayList(eapParam))) {
+ Log.e(TAG, ssid + ": failed to set eap identity: " + eapParam);
+ return false;
+ }
+ /** EAP Anonymous Identity */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ANON_IDENTITY_KEY);
+ if (!TextUtils.isEmpty(eapParam)
+ && !setEapAnonymousIdentity(NativeUtil.stringToByteArrayList(eapParam))) {
+ Log.e(TAG, ssid + ": failed to set eap anonymous identity: " + eapParam);
+ return false;
+ }
+ /** EAP Password */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.PASSWORD_KEY);
+ if (!TextUtils.isEmpty(eapParam)
+ && !setEapPassword(NativeUtil.stringToByteArrayList(eapParam))) {
+ Log.e(TAG, ssid + ": failed to set eap password");
+ return false;
+ }
+ /** EAP Client Cert */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.CLIENT_CERT_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapClientCert(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap client cert: " + eapParam);
+ return false;
+ }
+ /** EAP CA Cert */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapCACert(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap ca cert: " + eapParam);
+ return false;
+ }
+ /** EAP Subject Match */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.SUBJECT_MATCH_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapSubjectMatch(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap subject match: " + eapParam);
+ return false;
+ }
+ /** EAP Engine ID */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_ID_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapEngineID(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap engine id: " + eapParam);
+ return false;
+ }
+ /** EAP Engine */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ENGINE_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapEngine(
+ eapParam.equals(WifiEnterpriseConfig.ENGINE_ENABLE) ? true : false)) {
+ Log.e(TAG, ssid + ": failed to set eap engine: " + eapParam);
+ return false;
+ }
+ /** EAP Private Key */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.PRIVATE_KEY_ID_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapPrivateKeyId(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap private key: " + eapParam);
+ return false;
+ }
+ /** EAP Alt Subject Match */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.ALTSUBJECT_MATCH_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapAltSubjectMatch(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap alt subject match: " + eapParam);
+ return false;
+ }
+ /** EAP Domain Suffix Match */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.DOM_SUFFIX_MATCH_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapDomainSuffixMatch(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap domain suffix match: " + eapParam);
+ return false;
+ }
+ /** EAP CA Path*/
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.CA_PATH_KEY);
+ if (!TextUtils.isEmpty(eapParam) && !setEapCAPath(eapParam)) {
+ Log.e(TAG, ssid + ": failed to set eap ca path: " + eapParam);
+ return false;
+ }
+
+ /** EAP Proactive Key Caching */
+ eapParam = eapConfig.getFieldValue(WifiEnterpriseConfig.OPP_KEY_CACHING);
+ if (!TextUtils.isEmpty(eapParam)
+ && !setEapProactiveKeyCaching(eapParam.equals("1") ? true : false)) {
+ Log.e(TAG, ssid + ": failed to set proactive key caching: " + eapParam);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Maps WifiConfiguration Key Management BitSet to Supplicant HIDL bitmask int
+ * TODO(b/32571829): Update mapping when fast transition keys are added
+ * @return bitmask int describing the allowed Key Management schemes, readable by the Supplicant
+ * HIDL hal
+ */
+ private static int wifiConfigurationToSupplicantKeyMgmtMask(BitSet keyMgmt) {
+ int mask = 0;
+ for (int bit = keyMgmt.nextSetBit(0); bit != -1; bit = keyMgmt.nextSetBit(bit + 1)) {
+ switch (bit) {
+ case WifiConfiguration.KeyMgmt.NONE:
+ mask |= ISupplicantStaNetwork.KeyMgmtMask.NONE;
+ break;
+ case WifiConfiguration.KeyMgmt.WPA_PSK:
+ mask |= ISupplicantStaNetwork.KeyMgmtMask.WPA_PSK;
+ break;
+ case WifiConfiguration.KeyMgmt.WPA_EAP:
+ mask |= ISupplicantStaNetwork.KeyMgmtMask.WPA_EAP;
+ break;
+ case WifiConfiguration.KeyMgmt.IEEE8021X:
+ mask |= ISupplicantStaNetwork.KeyMgmtMask.IEEE8021X;
+ break;
+ case WifiConfiguration.KeyMgmt.OSEN:
+ mask |= ISupplicantStaNetwork.KeyMgmtMask.OSEN;
+ break;
+ case WifiConfiguration.KeyMgmt.FT_PSK:
+ mask |= ISupplicantStaNetwork.KeyMgmtMask.FT_PSK;
+ break;
+ case WifiConfiguration.KeyMgmt.FT_EAP:
+ mask |= ISupplicantStaNetwork.KeyMgmtMask.FT_EAP;
+ break;
+ case WifiConfiguration.KeyMgmt.WPA2_PSK: // This should never happen
+ default:
+ throw new IllegalArgumentException(
+ "Invalid protoMask bit in keyMgmt: " + bit);
+ }
+ }
+ return mask;
+ }
+
+ private static int wifiConfigurationToSupplicantProtoMask(BitSet protoMask) {
+ int mask = 0;
+ for (int bit = protoMask.nextSetBit(0); bit != -1; bit = protoMask.nextSetBit(bit + 1)) {
+ switch (bit) {
+ case WifiConfiguration.Protocol.WPA:
+ mask |= ISupplicantStaNetwork.ProtoMask.WPA;
+ break;
+ case WifiConfiguration.Protocol.RSN:
+ mask |= ISupplicantStaNetwork.ProtoMask.RSN;
+ break;
+ case WifiConfiguration.Protocol.OSEN:
+ mask |= ISupplicantStaNetwork.ProtoMask.OSEN;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid protoMask bit in wificonfig: " + bit);
+ }
+ }
+ return mask;
+ };
+
+ private static int wifiConfigurationToSupplicantAuthAlgMask(BitSet authAlgMask) {
+ int mask = 0;
+ for (int bit = authAlgMask.nextSetBit(0); bit != -1;
+ bit = authAlgMask.nextSetBit(bit + 1)) {
+ switch (bit) {
+ case WifiConfiguration.AuthAlgorithm.OPEN:
+ mask |= ISupplicantStaNetwork.AuthAlgMask.OPEN;
+ break;
+ case WifiConfiguration.AuthAlgorithm.SHARED:
+ mask |= ISupplicantStaNetwork.AuthAlgMask.SHARED;
+ break;
+ case WifiConfiguration.AuthAlgorithm.LEAP:
+ mask |= ISupplicantStaNetwork.AuthAlgMask.LEAP;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid authAlgMask bit in wificonfig: " + bit);
+ }
+ }
+ return mask;
+ };
+
+ private static int wifiConfigurationToSupplicantGroupCipherMask(BitSet groupCipherMask) {
+ int mask = 0;
+ for (int bit = groupCipherMask.nextSetBit(0); bit != -1; bit =
+ groupCipherMask.nextSetBit(bit + 1)) {
+ switch (bit) {
+ case WifiConfiguration.GroupCipher.WEP40:
+ mask |= ISupplicantStaNetwork.GroupCipherMask.WEP40;
+ break;
+ case WifiConfiguration.GroupCipher.WEP104:
+ mask |= ISupplicantStaNetwork.GroupCipherMask.WEP104;
+ break;
+ case WifiConfiguration.GroupCipher.TKIP:
+ mask |= ISupplicantStaNetwork.GroupCipherMask.TKIP;
+ break;
+ case WifiConfiguration.GroupCipher.CCMP:
+ mask |= ISupplicantStaNetwork.GroupCipherMask.CCMP;
+ break;
+ case WifiConfiguration.GroupCipher.GTK_NOT_USED:
+ mask |= ISupplicantStaNetwork.GroupCipherMask.GTK_NOT_USED;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid GroupCipherMask bit in wificonfig: " + bit);
+ }
+ }
+ return mask;
+ };
+
+ private static int wifiConfigurationToSupplicantPairwiseCipherMask(BitSet pairwiseCipherMask) {
+ int mask = 0;
+ for (int bit = pairwiseCipherMask.nextSetBit(0); bit != -1;
+ bit = pairwiseCipherMask.nextSetBit(bit + 1)) {
+ switch (bit) {
+ case WifiConfiguration.PairwiseCipher.NONE:
+ mask |= ISupplicantStaNetwork.PairwiseCipherMask.NONE;
+ break;
+ case WifiConfiguration.PairwiseCipher.TKIP:
+ mask |= ISupplicantStaNetwork.PairwiseCipherMask.TKIP;
+ break;
+ case WifiConfiguration.PairwiseCipher.CCMP:
+ mask |= ISupplicantStaNetwork.PairwiseCipherMask.CCMP;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid pairwiseCipherMask bit in wificonfig: " + bit);
+ }
+ }
+ return mask;
+ };
+
+ private static int supplicantToWifiConfigurationEapMethod(int value) {
+ switch (value) {
+ case ISupplicantStaNetwork.EapMethod.PEAP:
+ return WifiEnterpriseConfig.Eap.PEAP;
+ case ISupplicantStaNetwork.EapMethod.TLS:
+ return WifiEnterpriseConfig.Eap.TLS;
+ case ISupplicantStaNetwork.EapMethod.TTLS:
+ return WifiEnterpriseConfig.Eap.TTLS;
+ case ISupplicantStaNetwork.EapMethod.PWD:
+ return WifiEnterpriseConfig.Eap.PWD;
+ case ISupplicantStaNetwork.EapMethod.SIM:
+ return WifiEnterpriseConfig.Eap.SIM;
+ case ISupplicantStaNetwork.EapMethod.AKA:
+ return WifiEnterpriseConfig.Eap.AKA;
+ case ISupplicantStaNetwork.EapMethod.AKA_PRIME:
+ return WifiEnterpriseConfig.Eap.AKA_PRIME;
+ case ISupplicantStaNetwork.EapMethod.WFA_UNAUTH_TLS:
+ return WifiEnterpriseConfig.Eap.UNAUTH_TLS;
+ // WifiEnterpriseConfig.Eap.NONE:
+ default:
+ Log.e(TAG, "invalid eap method value from supplicant: " + value);
+ return -1;
+ }
+ };
+
+ private static int supplicantToWifiConfigurationEapPhase2Method(int value) {
+ switch (value) {
+ case ISupplicantStaNetwork.EapPhase2Method.NONE:
+ return WifiEnterpriseConfig.Phase2.NONE;
+ case ISupplicantStaNetwork.EapPhase2Method.PAP:
+ return WifiEnterpriseConfig.Phase2.PAP;
+ case ISupplicantStaNetwork.EapPhase2Method.MSPAP:
+ return WifiEnterpriseConfig.Phase2.MSCHAP;
+ case ISupplicantStaNetwork.EapPhase2Method.MSPAPV2:
+ return WifiEnterpriseConfig.Phase2.MSCHAPV2;
+ case ISupplicantStaNetwork.EapPhase2Method.GTC:
+ return WifiEnterpriseConfig.Phase2.GTC;
+ case ISupplicantStaNetwork.EapPhase2Method.SIM:
+ return WifiEnterpriseConfig.Phase2.SIM;
+ case ISupplicantStaNetwork.EapPhase2Method.AKA:
+ return WifiEnterpriseConfig.Phase2.AKA;
+ case ISupplicantStaNetwork.EapPhase2Method.AKA_PRIME:
+ return WifiEnterpriseConfig.Phase2.AKA_PRIME;
+ default:
+ Log.e(TAG, "invalid eap phase2 method value from supplicant: " + value);
+ return -1;
+ }
+ };
+
+ private static int supplicantMaskValueToWifiConfigurationBitSet(int supplicantMask,
+ int supplicantValue, BitSet bitset,
+ int bitSetPosition) {
+ bitset.set(bitSetPosition, (supplicantMask & supplicantValue) == supplicantValue);
+ int modifiedSupplicantMask = supplicantMask & ~supplicantValue;
+ return modifiedSupplicantMask;
+ }
+
+ private static BitSet supplicantToWifiConfigurationKeyMgmtMask(int mask) {
+ BitSet bitset = new BitSet();
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.KeyMgmtMask.NONE, bitset,
+ WifiConfiguration.KeyMgmt.NONE);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.KeyMgmtMask.WPA_PSK, bitset,
+ WifiConfiguration.KeyMgmt.WPA_PSK);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.KeyMgmtMask.WPA_EAP, bitset,
+ WifiConfiguration.KeyMgmt.WPA_EAP);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.KeyMgmtMask.IEEE8021X, bitset,
+ WifiConfiguration.KeyMgmt.IEEE8021X);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.KeyMgmtMask.OSEN, bitset,
+ WifiConfiguration.KeyMgmt.OSEN);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.KeyMgmtMask.FT_PSK, bitset,
+ WifiConfiguration.KeyMgmt.FT_PSK);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.KeyMgmtMask.FT_EAP, bitset,
+ WifiConfiguration.KeyMgmt.FT_EAP);
+ if (mask != 0) {
+ throw new IllegalArgumentException(
+ "invalid key mgmt mask from supplicant: " + mask);
+ }
+ return bitset;
+ }
+
+ private static BitSet supplicantToWifiConfigurationProtoMask(int mask) {
+ BitSet bitset = new BitSet();
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.ProtoMask.WPA, bitset,
+ WifiConfiguration.Protocol.WPA);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.ProtoMask.RSN, bitset,
+ WifiConfiguration.Protocol.RSN);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.ProtoMask.OSEN, bitset,
+ WifiConfiguration.Protocol.OSEN);
+ if (mask != 0) {
+ throw new IllegalArgumentException(
+ "invalid proto mask from supplicant: " + mask);
+ }
+ return bitset;
+ };
+
+ private static BitSet supplicantToWifiConfigurationAuthAlgMask(int mask) {
+ BitSet bitset = new BitSet();
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.AuthAlgMask.OPEN, bitset,
+ WifiConfiguration.AuthAlgorithm.OPEN);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.AuthAlgMask.SHARED, bitset,
+ WifiConfiguration.AuthAlgorithm.SHARED);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.AuthAlgMask.LEAP, bitset,
+ WifiConfiguration.AuthAlgorithm.LEAP);
+ if (mask != 0) {
+ throw new IllegalArgumentException(
+ "invalid auth alg mask from supplicant: " + mask);
+ }
+ return bitset;
+ };
+
+ private static BitSet supplicantToWifiConfigurationGroupCipherMask(int mask) {
+ BitSet bitset = new BitSet();
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.GroupCipherMask.WEP40, bitset,
+ WifiConfiguration.GroupCipher.WEP40);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.GroupCipherMask.WEP104, bitset,
+ WifiConfiguration.GroupCipher.WEP104);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.GroupCipherMask.TKIP, bitset,
+ WifiConfiguration.GroupCipher.TKIP);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.GroupCipherMask.CCMP, bitset,
+ WifiConfiguration.GroupCipher.CCMP);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.GroupCipherMask.GTK_NOT_USED, bitset,
+ WifiConfiguration.GroupCipher.GTK_NOT_USED);
+ if (mask != 0) {
+ throw new IllegalArgumentException(
+ "invalid group cipher mask from supplicant: " + mask);
+ }
+ return bitset;
+ };
+
+ private static BitSet supplicantToWifiConfigurationPairwiseCipherMask(int mask) {
+ BitSet bitset = new BitSet();
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.PairwiseCipherMask.NONE, bitset,
+ WifiConfiguration.PairwiseCipher.NONE);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.PairwiseCipherMask.TKIP, bitset,
+ WifiConfiguration.PairwiseCipher.TKIP);
+ mask = supplicantMaskValueToWifiConfigurationBitSet(
+ mask, ISupplicantStaNetwork.PairwiseCipherMask.CCMP, bitset,
+ WifiConfiguration.PairwiseCipher.CCMP);
+ if (mask != 0) {
+ throw new IllegalArgumentException(
+ "invalid pairwise cipher mask from supplicant: " + mask);
+ }
+ return bitset;
+ };
+
+ private static int wifiConfigurationToSupplicantEapMethod(int value) {
+ switch (value) {
+ case WifiEnterpriseConfig.Eap.PEAP:
+ return ISupplicantStaNetwork.EapMethod.PEAP;
+ case WifiEnterpriseConfig.Eap.TLS:
+ return ISupplicantStaNetwork.EapMethod.TLS;
+ case WifiEnterpriseConfig.Eap.TTLS:
+ return ISupplicantStaNetwork.EapMethod.TTLS;
+ case WifiEnterpriseConfig.Eap.PWD:
+ return ISupplicantStaNetwork.EapMethod.PWD;
+ case WifiEnterpriseConfig.Eap.SIM:
+ return ISupplicantStaNetwork.EapMethod.SIM;
+ case WifiEnterpriseConfig.Eap.AKA:
+ return ISupplicantStaNetwork.EapMethod.AKA;
+ case WifiEnterpriseConfig.Eap.AKA_PRIME:
+ return ISupplicantStaNetwork.EapMethod.AKA_PRIME;
+ case WifiEnterpriseConfig.Eap.UNAUTH_TLS:
+ return ISupplicantStaNetwork.EapMethod.WFA_UNAUTH_TLS;
+ // WifiEnterpriseConfig.Eap.NONE:
+ default:
+ Log.e(TAG, "invalid eap method value from WifiConfiguration: " + value);
+ return -1;
+ }
+ };
+
+ private static int wifiConfigurationToSupplicantEapPhase2Method(int value) {
+ switch (value) {
+ case WifiEnterpriseConfig.Phase2.NONE:
+ return ISupplicantStaNetwork.EapPhase2Method.NONE;
+ case WifiEnterpriseConfig.Phase2.PAP:
+ return ISupplicantStaNetwork.EapPhase2Method.PAP;
+ case WifiEnterpriseConfig.Phase2.MSCHAP:
+ return ISupplicantStaNetwork.EapPhase2Method.MSPAP;
+ case WifiEnterpriseConfig.Phase2.MSCHAPV2:
+ return ISupplicantStaNetwork.EapPhase2Method.MSPAPV2;
+ case WifiEnterpriseConfig.Phase2.GTC:
+ return ISupplicantStaNetwork.EapPhase2Method.GTC;
+ case WifiEnterpriseConfig.Phase2.SIM:
+ return ISupplicantStaNetwork.EapPhase2Method.SIM;
+ case WifiEnterpriseConfig.Phase2.AKA:
+ return ISupplicantStaNetwork.EapPhase2Method.AKA;
+ case WifiEnterpriseConfig.Phase2.AKA_PRIME:
+ return ISupplicantStaNetwork.EapPhase2Method.AKA_PRIME;
+ default:
+ Log.e(TAG, "invalid eap phase2 method value from WifiConfiguration: " + value);
+ return -1;
+ }
+ };
+
+ /** See ISupplicantNetwork.hal for documentation */
+ private boolean getId() {
+ synchronized (mLock) {
+ final String methodStr = "getId";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getId((SupplicantStatus status, int idValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mNetworkId = idValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean registerCallback(ISupplicantStaNetworkCallback callback) {
+ synchronized (mLock) {
+ final String methodStr = "registerCallback";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.registerCallback(callback);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setSsid(java.util.ArrayList<Byte> ssid) {
+ synchronized (mLock) {
+ final String methodStr = "setSsid";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setSsid(ssid);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set the BSSID for this network.
+ *
+ * @param bssidStr MAC address in "XX:XX:XX:XX:XX:XX" form or "any" to reset the mac address.
+ * @return true if it succeeds, false otherwise.
+ */
+ public boolean setBssid(String bssidStr) {
+ try {
+ return setBssid(NativeUtil.macAddressToByteArray(bssidStr));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + bssidStr, e);
+ return false;
+ }
+ }
+
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setBssid(byte[/* 6 */] bssid) {
+ synchronized (mLock) {
+ final String methodStr = "setBssid";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setBssid(bssid);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setScanSsid(boolean enable) {
+ synchronized (mLock) {
+ final String methodStr = "setScanSsid";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setScanSsid(enable);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setKeyMgmt(int keyMgmtMask) {
+ synchronized (mLock) {
+ final String methodStr = "setKeyMgmt";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setKeyMgmt(keyMgmtMask);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setProto(int protoMask) {
+ synchronized (mLock) {
+ final String methodStr = "setProto";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setProto(protoMask);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setAuthAlg(int authAlgMask) {
+ synchronized (mLock) {
+ final String methodStr = "setAuthAlg";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setAuthAlg(authAlgMask);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setGroupCipher(int groupCipherMask) {
+ synchronized (mLock) {
+ final String methodStr = "setGroupCipher";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setGroupCipher(groupCipherMask);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setPairwiseCipher(int pairwiseCipherMask) {
+ synchronized (mLock) {
+ final String methodStr = "setPairwiseCipher";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status =
+ mISupplicantStaNetwork.setPairwiseCipher(pairwiseCipherMask);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setPskPassphrase(String psk) {
+ synchronized (mLock) {
+ final String methodStr = "setPskPassphrase";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setPskPassphrase(psk);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setPsk(byte[] psk) {
+ synchronized (mLock) {
+ final String methodStr = "setPsk";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setPsk(psk);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setWepKey(int keyIdx, java.util.ArrayList<Byte> wepKey) {
+ synchronized (mLock) {
+ final String methodStr = "setWepKey";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setWepKey(keyIdx, wepKey);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setWepTxKeyIdx(int keyIdx) {
+ synchronized (mLock) {
+ final String methodStr = "setWepTxKeyIdx";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setWepTxKeyIdx(keyIdx);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setRequirePmf(boolean enable) {
+ synchronized (mLock) {
+ final String methodStr = "setRequirePmf";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setRequirePmf(enable);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setUpdateIdentifier(int identifier) {
+ synchronized (mLock) {
+ final String methodStr = "setUpdateIdentifier";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setUpdateIdentifier(identifier);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapMethod(int method) {
+ synchronized (mLock) {
+ final String methodStr = "setEapMethod";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapMethod(method);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapPhase2Method(int method) {
+ synchronized (mLock) {
+ final String methodStr = "setEapPhase2Method";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapPhase2Method(method);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapIdentity(java.util.ArrayList<Byte> identity) {
+ synchronized (mLock) {
+ final String methodStr = "setEapIdentity";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapIdentity(identity);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapAnonymousIdentity(java.util.ArrayList<Byte> identity) {
+ synchronized (mLock) {
+ final String methodStr = "setEapAnonymousIdentity";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapAnonymousIdentity(identity);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapPassword(java.util.ArrayList<Byte> password) {
+ synchronized (mLock) {
+ final String methodStr = "setEapPassword";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapPassword(password);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapCACert(String path) {
+ synchronized (mLock) {
+ final String methodStr = "setEapCACert";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapCACert(path);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapCAPath(String path) {
+ synchronized (mLock) {
+ final String methodStr = "setEapCAPath";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapCAPath(path);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapClientCert(String path) {
+ synchronized (mLock) {
+ final String methodStr = "setEapClientCert";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapClientCert(path);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapPrivateKeyId(String id) {
+ synchronized (mLock) {
+ final String methodStr = "setEapPrivateKeyId";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapPrivateKeyId(id);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapSubjectMatch(String match) {
+ synchronized (mLock) {
+ final String methodStr = "setEapSubjectMatch";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapSubjectMatch(match);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapAltSubjectMatch(String match) {
+ synchronized (mLock) {
+ final String methodStr = "setEapAltSubjectMatch";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapAltSubjectMatch(match);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapEngine(boolean enable) {
+ synchronized (mLock) {
+ final String methodStr = "setEapEngine";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapEngine(enable);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapEngineID(String id) {
+ synchronized (mLock) {
+ final String methodStr = "setEapEngineID";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapEngineID(id);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapDomainSuffixMatch(String match) {
+ synchronized (mLock) {
+ final String methodStr = "setEapDomainSuffixMatch";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setEapDomainSuffixMatch(match);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setEapProactiveKeyCaching(boolean enable) {
+ synchronized (mLock) {
+ final String methodStr = "setEapProactiveKeyCaching";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setProactiveKeyCaching(enable);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean setIdStr(String idString) {
+ synchronized (mLock) {
+ final String methodStr = "setIdStr";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.setIdStr(idString);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getSsid() {
+ synchronized (mLock) {
+ final String methodStr = "getSsid";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getSsid((SupplicantStatus status,
+ java.util.ArrayList<Byte> ssidValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mSsid = ssidValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getBssid() {
+ synchronized (mLock) {
+ final String methodStr = "getBssid";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getBssid((SupplicantStatus status,
+ byte[/* 6 */] bssidValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mBssid = bssidValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getScanSsid() {
+ synchronized (mLock) {
+ final String methodStr = "getScanSsid";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getScanSsid((SupplicantStatus status,
+ boolean enabledValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mScanSsid = enabledValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getKeyMgmt() {
+ synchronized (mLock) {
+ final String methodStr = "getKeyMgmt";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getKeyMgmt((SupplicantStatus status,
+ int keyMgmtMaskValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mKeyMgmtMask = keyMgmtMaskValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getProto() {
+ synchronized (mLock) {
+ final String methodStr = "getProto";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getProto((SupplicantStatus status, int protoMaskValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mProtoMask = protoMaskValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getAuthAlg() {
+ synchronized (mLock) {
+ final String methodStr = "getAuthAlg";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getAuthAlg((SupplicantStatus status,
+ int authAlgMaskValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mAuthAlgMask = authAlgMaskValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getGroupCipher() {
+ synchronized (mLock) {
+ final String methodStr = "getGroupCipher";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getGroupCipher((SupplicantStatus status,
+ int groupCipherMaskValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mGroupCipherMask = groupCipherMaskValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getPairwiseCipher() {
+ synchronized (mLock) {
+ final String methodStr = "getPairwiseCipher";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getPairwiseCipher((SupplicantStatus status,
+ int pairwiseCipherMaskValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mPairwiseCipherMask = pairwiseCipherMaskValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getPskPassphrase() {
+ synchronized (mLock) {
+ final String methodStr = "getPskPassphrase";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getPskPassphrase((SupplicantStatus status,
+ String pskValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mPskPassphrase = pskValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getPsk() {
+ synchronized (mLock) {
+ final String methodStr = "getPsk";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getPsk((SupplicantStatus status, byte[] pskValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mPsk = pskValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getWepKey(int keyIdx) {
+ synchronized (mLock) {
+ final String methodStr = "keyIdx";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getWepKey(keyIdx, (SupplicantStatus status,
+ java.util.ArrayList<Byte> wepKeyValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mWepKey = wepKeyValue;
+ } else {
+ Log.e(TAG, methodStr + ", failed: " + status.debugMessage);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getWepTxKeyIdx() {
+ synchronized (mLock) {
+ final String methodStr = "getWepTxKeyIdx";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getWepTxKeyIdx((SupplicantStatus status,
+ int keyIdxValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mWepTxKeyIdx = keyIdxValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getRequirePmf() {
+ synchronized (mLock) {
+ final String methodStr = "getRequirePmf";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getRequirePmf((SupplicantStatus status,
+ boolean enabledValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mRequirePmf = enabledValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapMethod() {
+ synchronized (mLock) {
+ final String methodStr = "getEapMethod";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapMethod((SupplicantStatus status,
+ int methodValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapMethod = methodValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapPhase2Method() {
+ synchronized (mLock) {
+ final String methodStr = "getEapPhase2Method";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapPhase2Method((SupplicantStatus status,
+ int methodValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapPhase2Method = methodValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapIdentity() {
+ synchronized (mLock) {
+ final String methodStr = "getEapIdentity";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapIdentity((SupplicantStatus status,
+ ArrayList<Byte> identityValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapIdentity = identityValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapAnonymousIdentity() {
+ synchronized (mLock) {
+ final String methodStr = "getEapAnonymousIdentity";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapAnonymousIdentity((SupplicantStatus status,
+ ArrayList<Byte> identityValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapAnonymousIdentity = identityValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * A wrapping method for getEapAnonymousIdentity().
+ * This get anonymous identity from supplicant and returns it as a string.
+ *
+ * @return anonymous identity string if succeeds, null otherwise.
+ */
+ public String fetchEapAnonymousIdentity() {
+ if (!getEapAnonymousIdentity()) {
+ return null;
+ }
+ return NativeUtil.stringFromByteArrayList(mEapAnonymousIdentity);
+ }
+
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapPassword() {
+ synchronized (mLock) {
+ final String methodStr = "getEapPassword";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapPassword((SupplicantStatus status,
+ ArrayList<Byte> passwordValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapPassword = passwordValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapCACert() {
+ synchronized (mLock) {
+ final String methodStr = "getEapCACert";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapCACert((SupplicantStatus status, String pathValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapCACert = pathValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapCAPath() {
+ synchronized (mLock) {
+ final String methodStr = "getEapCAPath";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapCAPath((SupplicantStatus status, String pathValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapCAPath = pathValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapClientCert() {
+ synchronized (mLock) {
+ final String methodStr = "getEapClientCert";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapClientCert((SupplicantStatus status,
+ String pathValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapClientCert = pathValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapPrivateKeyId() {
+ synchronized (mLock) {
+ final String methodStr = "getEapPrivateKeyId";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapPrivateKeyId((SupplicantStatus status,
+ String idValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapPrivateKeyId = idValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapSubjectMatch() {
+ synchronized (mLock) {
+ final String methodStr = "getEapSubjectMatch";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapSubjectMatch((SupplicantStatus status,
+ String matchValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapSubjectMatch = matchValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapAltSubjectMatch() {
+ synchronized (mLock) {
+ final String methodStr = "getEapAltSubjectMatch";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapAltSubjectMatch((SupplicantStatus status,
+ String matchValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapAltSubjectMatch = matchValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapEngine() {
+ synchronized (mLock) {
+ final String methodStr = "getEapEngine";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapEngine((SupplicantStatus status,
+ boolean enabledValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapEngine = enabledValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapEngineID() {
+ synchronized (mLock) {
+ final String methodStr = "getEapEngineID";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapEngineID((SupplicantStatus status, String idValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapEngineID = idValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getEapDomainSuffixMatch() {
+ synchronized (mLock) {
+ final String methodStr = "getEapDomainSuffixMatch";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getEapDomainSuffixMatch((SupplicantStatus status,
+ String matchValue) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mEapDomainSuffixMatch = matchValue;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean getIdStr() {
+ synchronized (mLock) {
+ final String methodStr = "getIdStr";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ MutableBoolean statusOk = new MutableBoolean(false);
+ mISupplicantStaNetwork.getIdStr((SupplicantStatus status, String idString) -> {
+ statusOk.value = status.code == SupplicantStatusCode.SUCCESS;
+ if (statusOk.value) {
+ this.mIdStr = idString;
+ } else {
+ checkStatusAndLogFailure(status, methodStr);
+ }
+ });
+ return statusOk.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean enable(boolean noConnect) {
+ synchronized (mLock) {
+ final String methodStr = "enable";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.enable(noConnect);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean disable() {
+ synchronized (mLock) {
+ final String methodStr = "disable";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.disable();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Trigger a connection to this network.
+ *
+ * @return true if it succeeds, false otherwise.
+ */
+ public boolean select() {
+ synchronized (mLock) {
+ final String methodStr = "select";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.select();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Send GSM auth response.
+ *
+ * @param paramsStr Response params as a string.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendNetworkEapSimGsmAuthResponse(String paramsStr) {
+ try {
+ Matcher match = GSM_AUTH_RESPONSE_PARAMS_PATTERN.matcher(paramsStr);
+ ArrayList<ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams> params =
+ new ArrayList<>();
+ while (match.find()) {
+ if (match.groupCount() != 2) {
+ Log.e(TAG, "Malformed gsm auth response params: " + paramsStr);
+ return false;
+ }
+ ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams param =
+ new ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams();
+ byte[] kc = NativeUtil.hexStringToByteArray(match.group(1));
+ if (kc == null || kc.length != param.kc.length) {
+ Log.e(TAG, "Invalid kc value: " + match.group(1));
+ return false;
+ }
+ byte[] sres = NativeUtil.hexStringToByteArray(match.group(2));
+ if (sres == null || sres.length != param.sres.length) {
+ Log.e(TAG, "Invalid sres value: " + match.group(2));
+ return false;
+ }
+ System.arraycopy(kc, 0, param.kc, 0, param.kc.length);
+ System.arraycopy(sres, 0, param.sres, 0, param.sres.length);
+ params.add(param);
+ }
+ // The number of kc/sres pairs can either be 2 or 3 depending on the request.
+ if (params.size() > 3 || params.size() < 2) {
+ Log.e(TAG, "Malformed gsm auth response params: " + paramsStr);
+ return false;
+ }
+ return sendNetworkEapSimGsmAuthResponse(params);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + paramsStr, e);
+ return false;
+ }
+ }
+
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean sendNetworkEapSimGsmAuthResponse(
+ ArrayList<ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams> params) {
+ synchronized (mLock) {
+ final String methodStr = "sendNetworkEapSimGsmAuthResponse";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status =
+ mISupplicantStaNetwork.sendNetworkEapSimGsmAuthResponse(params);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ public boolean sendNetworkEapSimGsmAuthFailure() {
+ synchronized (mLock) {
+ final String methodStr = "sendNetworkEapSimGsmAuthFailure";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.sendNetworkEapSimGsmAuthFailure();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /**
+ * Send UMTS auth response.
+ *
+ * @param paramsStr Response params as a string.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendNetworkEapSimUmtsAuthResponse(String paramsStr) {
+ try {
+ Matcher match = UMTS_AUTH_RESPONSE_PARAMS_PATTERN.matcher(paramsStr);
+ if (!match.find() || match.groupCount() != 3) {
+ Log.e(TAG, "Malformed umts auth response params: " + paramsStr);
+ return false;
+ }
+ ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams params =
+ new ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams();
+ byte[] ik = NativeUtil.hexStringToByteArray(match.group(1));
+ if (ik == null || ik.length != params.ik.length) {
+ Log.e(TAG, "Invalid ik value: " + match.group(1));
+ return false;
+ }
+ byte[] ck = NativeUtil.hexStringToByteArray(match.group(2));
+ if (ck == null || ck.length != params.ck.length) {
+ Log.e(TAG, "Invalid ck value: " + match.group(2));
+ return false;
+ }
+ byte[] res = NativeUtil.hexStringToByteArray(match.group(3));
+ if (res == null || res.length == 0) {
+ Log.e(TAG, "Invalid res value: " + match.group(3));
+ return false;
+ }
+ System.arraycopy(ik, 0, params.ik, 0, params.ik.length);
+ System.arraycopy(ck, 0, params.ck, 0, params.ck.length);
+ for (byte b : res) {
+ params.res.add(b);
+ }
+ return sendNetworkEapSimUmtsAuthResponse(params);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + paramsStr, e);
+ return false;
+ }
+ }
+
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean sendNetworkEapSimUmtsAuthResponse(
+ ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams params) {
+ synchronized (mLock) {
+ final String methodStr = "sendNetworkEapSimUmtsAuthResponse";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status =
+ mISupplicantStaNetwork.sendNetworkEapSimUmtsAuthResponse(params);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /**
+ * Send UMTS auts response.
+ *
+ * @param paramsStr Response params as a string.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendNetworkEapSimUmtsAutsResponse(String paramsStr) {
+ try {
+ Matcher match = UMTS_AUTS_RESPONSE_PARAMS_PATTERN.matcher(paramsStr);
+ if (!match.find() || match.groupCount() != 1) {
+ Log.e(TAG, "Malformed umts auts response params: " + paramsStr);
+ return false;
+ }
+ byte[] auts = NativeUtil.hexStringToByteArray(match.group(1));
+ if (auts == null || auts.length != 14) {
+ Log.e(TAG, "Invalid auts value: " + match.group(1));
+ return false;
+ }
+ return sendNetworkEapSimUmtsAutsResponse(auts);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + paramsStr, e);
+ return false;
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean sendNetworkEapSimUmtsAutsResponse(byte[/* 14 */] auts) {
+ synchronized (mLock) {
+ final String methodStr = "sendNetworkEapSimUmtsAutsResponse";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status =
+ mISupplicantStaNetwork.sendNetworkEapSimUmtsAutsResponse(auts);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ public boolean sendNetworkEapSimUmtsAuthFailure() {
+ synchronized (mLock) {
+ final String methodStr = "sendNetworkEapSimUmtsAuthFailure";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status = mISupplicantStaNetwork.sendNetworkEapSimUmtsAuthFailure();
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+ /**
+ * Send eap identity response.
+ *
+ * @param identityStr Identity as a string.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean sendNetworkEapIdentityResponse(String identityStr) {
+ try {
+ ArrayList<Byte> identity = NativeUtil.stringToByteArrayList(identityStr);
+ return sendNetworkEapIdentityResponse(identity);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + identityStr, e);
+ return false;
+ }
+ }
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private boolean sendNetworkEapIdentityResponse(ArrayList<Byte> identity) {
+ synchronized (mLock) {
+ final String methodStr = "sendNetworkEapIdentityResponse";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
+ try {
+ SupplicantStatus status =
+ mISupplicantStaNetwork.sendNetworkEapIdentityResponse(identity);
+ return checkStatusAndLogFailure(status, methodStr);
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Retrieve the NFC token for this network.
+ *
+ * @return Hex string corresponding to the NFC token or null for failure.
+ */
+ public String getWpsNfcConfigurationToken() {
+ ArrayList<Byte> token = getWpsNfcConfigurationTokenInternal();
+ if (token == null) {
+ return null;
+ }
+ return NativeUtil.hexStringFromByteArray(NativeUtil.byteArrayFromArrayList(token));
+ }
+
+ /** See ISupplicantStaNetwork.hal for documentation */
+ private ArrayList<Byte> getWpsNfcConfigurationTokenInternal() {
+ synchronized (mLock) {
+ final String methodStr = "getWpsNfcConfigurationToken";
+ if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return null;
+ final Mutable<ArrayList<Byte>> gotToken = new Mutable<>();
+ try {
+ mISupplicantStaNetwork.getWpsNfcConfigurationToken(
+ (SupplicantStatus status, ArrayList<Byte> token) -> {
+ if (checkStatusAndLogFailure(status, methodStr)) {
+ gotToken.value = token;
+ }
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e, methodStr);
+ }
+ return gotToken.value;
+ }
+ }
+
+ /**
+ * Returns true if provided status code is SUCCESS, logs debug message and returns false
+ * otherwise
+ */
+ private boolean checkStatusAndLogFailure(SupplicantStatus status, final String methodStr) {
+ if (status.code != SupplicantStatusCode.SUCCESS) {
+ Log.e(TAG, "ISupplicantStaNetwork." + methodStr + " failed: "
+ + SupplicantStaIfaceHal.supplicantStatusCodeToString(status.code) + ", "
+ + status.debugMessage);
+ return false;
+ } else {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ISupplicantStaNetwork." + methodStr + " succeeded");
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Helper function to log callbacks.
+ */
+ private void logCallback(final String methodStr) {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "ISupplicantStaNetworkCallback." + methodStr + " received");
+ }
+ }
+
+ /**
+ * Returns false if ISupplicantStaNetwork is null, and logs failure of methodStr
+ */
+ private boolean checkISupplicantStaNetworkAndLogFailure(final String methodStr) {
+ if (mISupplicantStaNetwork == null) {
+ Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaNetwork is null");
+ return false;
+ }
+ return true;
+ }
+
+ private void handleRemoteException(RemoteException e, String methodStr) {
+ mISupplicantStaNetwork = null;
+ Log.e(TAG, "ISupplicantStaNetwork." + methodStr + " failed with exception", e);
+ }
+
+ /**
+ * Adds FT flags for networks if the device supports it.
+ */
+ private BitSet addFastTransitionFlags(BitSet keyManagementFlags) {
+ if (!mSystemSupportsFastBssTransition) {
+ return keyManagementFlags;
+ }
+ BitSet modifiedFlags = (BitSet) keyManagementFlags.clone();
+ if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_PSK);
+ }
+ if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
+ modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_EAP);
+ }
+ return modifiedFlags;
+ }
+
+ /**
+ * Removes FT flags for networks if the device supports it.
+ */
+ private BitSet removeFastTransitionFlags(BitSet keyManagementFlags) {
+ BitSet modifiedFlags = (BitSet) keyManagementFlags.clone();
+ modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_PSK);
+ modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_EAP);
+ return modifiedFlags;
+ }
+
+ /**
+ * Creates the JSON encoded network extra using the map of string key, value pairs.
+ */
+ 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 null;
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
+ return null;
+ }
+ return encoded;
+ }
+
+ /**
+ * Parse the network extra JSON encoded string to a map of string key, value pairs.
+ */
+ public static Map<String, String> parseNetworkExtra(String encoded) {
+ if (TextUtils.isEmpty(encoded)) {
+ return null;
+ }
+ try {
+ // 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
+ // constructor will thrown a JSONException and the method will return null.
+ final JSONObject json = new JSONObject(URLDecoder.decode(encoded, "UTF-8"));
+ final Map<String, String> values = new HashMap<>();
+ final Iterator<?> it = json.keys();
+ while (it.hasNext()) {
+ final String key = (String) it.next();
+ final Object value = json.get(key);
+ if (value instanceof String) {
+ values.put(key, (String) value);
+ }
+ }
+ return values;
+ } catch (UnsupportedEncodingException e) {
+ Log.e(TAG, "Unable to deserialize networkExtra: " + e.toString());
+ return null;
+ } catch (JSONException e) {
+ // This is not necessarily an error. This exception will also occur if we encounter a
+ // legacy FQDN stored as a plain string. We want to return null in this case as no JSON
+ // dictionary of extras was found.
+ return null;
+ }
+ }
+
+ private static class Mutable<E> {
+ public E value;
+
+ Mutable() {
+ value = null;
+ }
+
+ Mutable(E value) {
+ this.value = value;
+ }
+ }
+
+ private class SupplicantStaNetworkHalCallback extends ISupplicantStaNetworkCallback.Stub {
+ /**
+ * Current configured network's framework network id.
+ */
+ private final int mFramewokNetworkId;
+ /**
+ * Current configured network's ssid.
+ */
+ private final String mSsid;
+
+ SupplicantStaNetworkHalCallback(int framewokNetworkId, String ssid) {
+ mFramewokNetworkId = framewokNetworkId;
+ mSsid = ssid;
+ }
+
+ @Override
+ public void onNetworkEapSimGsmAuthRequest(
+ ISupplicantStaNetworkCallback.NetworkRequestEapSimGsmAuthParams params) {
+ logCallback("onNetworkEapSimGsmAuthRequest");
+ synchronized (mLock) {
+ String[] data = new String[params.rands.size()];
+ int i = 0;
+ for (byte[] rand : params.rands) {
+ data[i++] = NativeUtil.hexStringFromByteArray(rand);
+ }
+ mWifiMonitor.broadcastNetworkGsmAuthRequestEvent(
+ mIfaceName, mFramewokNetworkId, mSsid, data);
+ }
+ }
+
+ @Override
+ public void onNetworkEapSimUmtsAuthRequest(
+ ISupplicantStaNetworkCallback.NetworkRequestEapSimUmtsAuthParams params) {
+ logCallback("onNetworkEapSimUmtsAuthRequest");
+ synchronized (mLock) {
+ String randHex = NativeUtil.hexStringFromByteArray(params.rand);
+ String autnHex = NativeUtil.hexStringFromByteArray(params.autn);
+ String[] data = {randHex, autnHex};
+ mWifiMonitor.broadcastNetworkUmtsAuthRequestEvent(
+ mIfaceName, mFramewokNetworkId, mSsid, data);
+ }
+ }
+
+ @Override
+ public void onNetworkEapIdentityRequest() {
+ logCallback("onNetworkEapIdentityRequest");
+ synchronized (mLock) {
+ mWifiMonitor.broadcastNetworkIdentityRequestEvent(
+ mIfaceName, mFramewokNetworkId, mSsid);
+ }
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/SupplicantStateTracker.java b/service/java/com/android/server/wifi/SupplicantStateTracker.java
index a7c21bf..cac7f06 100644
--- a/service/java/com/android/server/wifi/SupplicantStateTracker.java
+++ b/service/java/com/android/server/wifi/SupplicantStateTracker.java
@@ -26,7 +26,6 @@
import android.os.Message;
import android.os.Parcelable;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;
import android.util.Slog;
@@ -49,12 +48,21 @@
private static final String TAG = "SupplicantStateTracker";
private static boolean DBG = false;
private final WifiConfigManager mWifiConfigManager;
+ private FrameworkFacade mFacade;
private final IBatteryStats mBatteryStats;
/* Indicates authentication failure in supplicant broadcast.
* TODO: enhance auth failure reporting to include notification
* for all type of failures: EAP, WPS & WPA networks */
private boolean mAuthFailureInSupplicantBroadcast = false;
+ /* Authentication failure reason
+ * see {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_NONE},
+ * {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_TIMEOUT},
+ * {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_WRONG_PSWD},
+ * {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_EAP_FAILURE}
+ */
+ private int mAuthFailureReason;
+
/* Maximum retries on a authentication failure notification */
private static final int MAX_RETRIES_ON_AUTHENTICATION_FAILURE = 2;
@@ -71,6 +79,7 @@
private final State mInactiveState = new InactiveState();
private final State mDisconnectState = new DisconnectedState();
private final State mScanState = new ScanState();
+ private final State mConnectionActiveState = new ConnectionActiveState();
private final State mHandshakeState = new HandshakeState();
private final State mCompletedState = new CompletedState();
private final State mDormantState = new DormantState();
@@ -87,20 +96,25 @@
return getCurrentState().getName();
}
- public SupplicantStateTracker(Context c, WifiConfigManager wcs, Handler t) {
+ public SupplicantStateTracker(Context c, WifiConfigManager wcs,
+ FrameworkFacade facade, Handler t) {
super(TAG, t.getLooper());
mContext = c;
mWifiConfigManager = wcs;
- mBatteryStats = (IBatteryStats)ServiceManager.getService(BatteryStats.SERVICE_NAME);
+ mFacade = facade;
+ mBatteryStats = mFacade.getBatteryService();
+ // CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mUninitializedState, mDefaultState);
addState(mInactiveState, mDefaultState);
addState(mDisconnectState, mDefaultState);
- addState(mScanState, mDefaultState);
- addState(mHandshakeState, mDefaultState);
- addState(mCompletedState, mDefaultState);
- addState(mDormantState, mDefaultState);
+ addState(mConnectionActiveState, mDefaultState);
+ addState(mScanState, mConnectionActiveState);
+ addState(mHandshakeState, mConnectionActiveState);
+ addState(mCompletedState, mConnectionActiveState);
+ addState(mDormantState, mConnectionActiveState);
+ // CHECKSTYLE:ON IndentationCheck
setInitialState(mUninitializedState);
setLogRecSize(50);
@@ -118,9 +132,7 @@
/* If other networks disabled during connection, enable them */
if (mNetworksDisabledDuringConnect) {
- mWifiConfigManager.enableAllNetworks();
- mNetworksDisabledDuringConnect = false;
- }
+ mNetworksDisabledDuringConnect = false; }
/* update network status */
mWifiConfigManager.updateNetworkSelectionStatus(netId, disableReason);
}
@@ -167,6 +179,11 @@
}
private void sendSupplicantStateChangedBroadcast(SupplicantState state, boolean failedAuth) {
+ sendSupplicantStateChangedBroadcast(state, failedAuth, WifiManager.ERROR_AUTH_FAILURE_NONE);
+ }
+
+ private void sendSupplicantStateChangedBroadcast(SupplicantState state, boolean failedAuth,
+ int reasonCode) {
int supplState;
switch (state) {
case DISCONNECTED: supplState = BatteryStats.WIFI_SUPPL_STATE_DISCONNECTED; break;
@@ -200,8 +217,11 @@
intent.putExtra(WifiManager.EXTRA_NEW_STATE, (Parcelable) state);
if (failedAuth) {
intent.putExtra(
- WifiManager.EXTRA_SUPPLICANT_ERROR,
- WifiManager.ERROR_AUTHENTICATING);
+ WifiManager.EXTRA_SUPPLICANT_ERROR,
+ WifiManager.ERROR_AUTHENTICATING);
+ intent.putExtra(
+ WifiManager.EXTRA_SUPPLICANT_ERROR_REASON,
+ reasonCode);
}
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -221,12 +241,15 @@
switch (message.what) {
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
mAuthFailureInSupplicantBroadcast = true;
+ mAuthFailureReason = message.arg2;
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
SupplicantState state = stateChangeResult.state;
- sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast);
+ sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast,
+ mAuthFailureReason);
mAuthFailureInSupplicantBroadcast = false;
+ mAuthFailureReason = WifiManager.ERROR_AUTH_FAILURE_NONE;
transitionOnSupplicantStateChange(stateChangeResult);
break;
case WifiStateMachine.CMD_RESET_SUPPLICANT_STATE:
@@ -280,6 +303,19 @@
}
}
+ /* Meta-state that processes supplicant disconnections and broadcasts this event. */
+ class ConnectionActiveState extends State {
+ @Override
+ public boolean processMessage(Message message) {
+ if (message.what == WifiStateMachine.CMD_RESET_SUPPLICANT_STATE) {
+ sendSupplicantStateChangedBroadcast(SupplicantState.DISCONNECTED, false);
+ }
+
+ /* Let parent states handle the state possible transition. */
+ return NOT_HANDLED;
+ }
+ }
+
class HandshakeState extends State {
/**
* The max number of the WPA supplicant loop iterations before we
@@ -315,7 +351,7 @@
}
mLoopDetectIndex = state.ordinal();
sendSupplicantStateChangedBroadcast(state,
- mAuthFailureInSupplicantBroadcast);
+ mAuthFailureInSupplicantBroadcast, mAuthFailureReason);
} else {
//Have the DefaultState handle the transition
return NOT_HANDLED;
@@ -334,7 +370,6 @@
if (DBG) Log.d(TAG, getName() + "\n");
/* Reset authentication failure count */
if (mNetworksDisabledDuringConnect) {
- mWifiConfigManager.enableAllNetworks();
mNetworksDisabledDuringConnect = false;
}
}
@@ -345,7 +380,8 @@
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
SupplicantState state = stateChangeResult.state;
- sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast);
+ sendSupplicantStateChangedBroadcast(state, mAuthFailureInSupplicantBroadcast,
+ mAuthFailureReason);
/* Ignore any connecting state in completed state. Group re-keying
* events and other auth events that do not affect connectivity are
* ignored
@@ -355,10 +391,6 @@
}
transitionOnSupplicantStateChange(stateChangeResult);
break;
- case WifiStateMachine.CMD_RESET_SUPPLICANT_STATE:
- sendSupplicantStateChangedBroadcast(SupplicantState.DISCONNECTED, false);
- transitionTo(mUninitializedState);
- break;
default:
return NOT_HANDLED;
}
@@ -378,6 +410,7 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
super.dump(fd, pw, args);
pw.println("mAuthFailureInSupplicantBroadcast " + mAuthFailureInSupplicantBroadcast);
+ pw.println("mAuthFailureReason " + mAuthFailureReason);
pw.println("mNetworksDisabledDuringConnect " + mNetworksDisabledDuringConnect);
pw.println();
}
diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java
index bcd8d03..9c90bcf 100644
--- a/service/java/com/android/server/wifi/WifiApConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiApConfigStore.java
@@ -32,6 +32,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Random;
import java.util.UUID;
/**
@@ -46,6 +47,9 @@
private static final int AP_CONFIG_FILE_VERSION = 2;
+ private static final int RAND_SSID_INT_MIN = 1000;
+ private static final int RAND_SSID_INT_MAX = 9999;
+
private WifiConfiguration mWifiApConfig = null;
private ArrayList<Integer> mAllowed2GChannel = null;
@@ -191,11 +195,33 @@
private WifiConfiguration getDefaultApConfiguration() {
WifiConfiguration config = new WifiConfiguration();
config.SSID = mContext.getResources().getString(
- R.string.wifi_tether_configure_ssid_default);
+ R.string.wifi_tether_configure_ssid_default) + "_" + getRandomIntForDefaultSsid();
config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
String randomUUID = UUID.randomUUID().toString();
//first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
return config;
}
+
+ private static int getRandomIntForDefaultSsid() {
+ Random random = new Random();
+ return random.nextInt((RAND_SSID_INT_MAX - RAND_SSID_INT_MIN) + 1) + RAND_SSID_INT_MIN;
+ }
+
+ /**
+ * Generate a temporary WPA2 based configuration for use by the local only hotspot.
+ * This config is not persisted and will not be stored by the WifiApConfigStore.
+ */
+ public static WifiConfiguration generateLocalOnlyHotspotConfig(Context context) {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = context.getResources().getString(
+ R.string.wifi_localhotspot_configure_ssid_default) + "_"
+ + getRandomIntForDefaultSsid();
+ config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
+ config.networkId = WifiConfiguration.LOCAL_ONLY_NETWORK_ID;
+ String randomUUID = UUID.randomUUID().toString();
+ // first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
+ config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
+ return config;
+ }
}
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..60c3b48
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiBackupRestore.java
@@ -0,0 +1,776 @@
+/*
+ * 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.os.Process;
+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.NativeUtil;
+import com.android.server.wifi.util.WifiPermissionsUtil;
+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";
+
+ private final WifiPermissionsUtil mWifiPermissionsUtil;
+ /**
+ * 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;
+
+ public WifiBackupRestore(WifiPermissionsUtil wifiPermissionsUtil) {
+ mWifiPermissionsUtil = wifiPermissionsUtil;
+ }
+
+ /**
+ * 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 new byte[0];
+ }
+
+ 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 new byte[0];
+ }
+
+ /**
+ * 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()) {
+ continue;
+ }
+ if (!mWifiPermissionsUtil.checkConfigOverridePermission(configuration.creatorUid)) {
+ Log.d(TAG, "Ignoring network from an app with no config override permission: "
+ + 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 = "id_str";
+
+ /**
+ * 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 processing 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);
+ if (idString != null) {
+ Map<String, String> extras =
+ SupplicantStaNetworkHal.parseNetworkExtra(
+ NativeUtil.removeEnclosingQuotes(idString));
+ String configKey = extras.get(
+ SupplicantStaNetworkHal.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());
+ }
+ // For wpa_supplicant backup data, parse out the creatorUid to ensure that
+ // these networks were created by system apps.
+ int creatorUid =
+ Integer.parseInt(extras.get(
+ SupplicantStaNetworkHal.ID_STRING_KEY_CREATOR_UID));
+ if (creatorUid >= Process.FIRST_APPLICATION_UID) {
+ Log.d(TAG, "Ignoring network from non-system app: "
+ + configuration.configKey());
+ return null;
+ }
+ }
+ }
+ 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 5eff02e..25a5a20 100644
--- a/service/java/com/android/server/wifi/WifiConfigManager.java
+++ b/service/java/com/android/server/wifi/WifiConfigManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -16,8 +16,7 @@
package com.android.server.wifi;
-import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
-
+import android.app.ActivityManager;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.ContentResolver;
@@ -25,1234 +24,1118 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
import android.net.IpConfiguration;
-import android.net.IpConfiguration.IpAssignment;
-import android.net.IpConfiguration.ProxySettings;
-import android.net.NetworkInfo.DetailedState;
import android.net.ProxyInfo;
import android.net.StaticIpConfiguration;
-import android.net.wifi.PasspointManagementObjectDefinition;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WifiConfiguration.Status;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
-import android.net.wifi.WpsInfo;
-import android.net.wifi.WpsResult;
-import android.os.Environment;
-import android.os.RemoteException;
-import android.os.SystemClock;
+import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.security.KeyStore;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
-import com.android.server.net.DelayedDiskWrite;
-import com.android.server.net.IpConfigStore;
-import com.android.server.wifi.anqp.ANQPElement;
-import com.android.server.wifi.anqp.ANQPFactory;
-import com.android.server.wifi.anqp.Constants;
-import com.android.server.wifi.hotspot2.ANQPData;
-import com.android.server.wifi.hotspot2.AnqpCache;
-import com.android.server.wifi.hotspot2.IconEvent;
-import com.android.server.wifi.hotspot2.NetworkDetail;
-import com.android.server.wifi.hotspot2.PasspointMatch;
-import com.android.server.wifi.hotspot2.SupplicantBridge;
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager;
-import com.android.server.wifi.hotspot2.pps.Credential;
-import com.android.server.wifi.hotspot2.pps.HomeSP;
+import com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy;
+import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.util.ScanResultUtil;
+import com.android.server.wifi.util.TelephonyUtil;
+import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
-import org.xml.sax.SAXException;
+import org.xmlpull.v1.XmlPullParserException;
-import java.io.BufferedReader;
-import java.io.DataOutputStream;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
-import java.security.cert.X509Certificate;
-import java.text.DateFormat;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.zip.CRC32;
-import java.util.zip.Checksum;
-
/**
- * This class provides the API to manage configured
- * wifi networks. The API is not thread safe is being
- * used only from WifiStateMachine.
+ * 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.
*
- * It deals with the following
- * - Add/update/remove a WifiConfiguration
- * The configuration contains two types of information.
- * = IP and proxy configuration that is handled by WifiConfigManager and
- * is saved to disk on any change.
+ * 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.
*
- * The format of configuration file is as follows:
- * <version>
- * <netA_key1><netA_value1><netA_key2><netA_value2>...<EOS>
- * <netB_key1><netB_value1><netB_key2><netB_value2>...<EOS>
- * ..
- *
- * (key, value) pairs for a given network are grouped together and can
- * be in any order. A EOS at the end of a set of (key, value) pairs
- * indicates that the next set of (key, value) pairs are for a new
- * network. A network is identified by a unique ID_KEY. If there is no
- * ID_KEY in the (key, value) pairs, the data is discarded.
- *
- * An invalid version on read would result in discarding the contents of
- * the file. On the next write, the latest version is written to file.
- *
- * Any failures during read or write to the configuration file are ignored
- * without reporting to the user since the likelihood of these errors are
- * low and the impact on connectivity is low.
- *
- * = SSID & security details that is pushed to the supplicant.
- * supplicant saves these details to the disk on calling
- * saveConfigCommand().
- *
- * We have two kinds of APIs exposed:
- * > public API calls that provide fine grained control
- * - enableNetwork, disableNetwork, addOrUpdateNetwork(),
- * removeNetwork(). For these calls, the config is not persisted
- * to the disk. (TODO: deprecate these calls in WifiManager)
- * > The new API calls - selectNetwork(), saveNetwork() & forgetNetwork().
- * These calls persist the supplicant config to disk.
- *
- * - Maintain a list of configured networks for quick access
- *
+ * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread.
*/
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;
- public static final int MAX_TX_PACKET_FOR_PARTIAL_SCANS = 40;
- public static final int MAX_RX_PACKET_FOR_PARTIAL_SCANS = 80;
- public static final boolean ROAM_ON_ANY = false;
- public static final int MAX_NUM_SCAN_CACHE_ENTRIES = 128;
- private static final boolean DBG = true;
- private static final String PPS_FILE = "/data/misc/wifi/PerProviderSubscription.conf";
- private static final String IP_CONFIG_FILE =
- Environment.getDataDirectory() + "/misc/wifi/ipconfig.txt";
-
- // The Wifi verbose log is provided as a way to persist the verbose logging settings
- // for testing purpose.
- // It is not intended for normal use.
- private static final String WIFI_VERBOSE_LOGS_KEY = "WIFI_VERBOSE_LOGS";
-
- // As we keep deleted PSK WifiConfiguration for a while, the PSK of
- // those deleted WifiConfiguration is set to this random unused PSK
- private static final String DELETED_CONFIG_PSK = "Mjkd86jEMGn79KhKll298Uu7-deleted";
-
/**
- * The maximum number of times we will retry a connection to an access point
- * for which we have failed in acquiring an IP address from DHCP. A value of
- * N means that we will make N+1 connection attempts in all.
- * <p>
- * See {@link Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}. This is the default
- * value if a Settings value is not present.
+ * String used to mask passwords to public interface.
*/
- private static final int DEFAULT_MAX_DHCP_RETRIES = 9;
-
+ @VisibleForTesting
+ public static final String PASSWORD_MASK = "*";
/**
- * 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.
+ * Package name for SysUI. This is used to lookup the UID of SysUI which is used to allow
+ * Quick settings to modify network configurations.
*/
- private static final int[] NETWORK_SELECTION_DISABLE_THRESHOLD = {
+ @VisibleForTesting
+ public static final String SYSUI_PACKAGE_NAME = "com.android.systemui";
+ /**
+ * 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 (deprecated)
+ 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_WIFI_MANAGER
+ 1 // threshold for DISABLED_BY_USER_SWITCH
};
-
/**
- * Timeout for each kind of error. After the timeout minutes, unblock the network again.
+ * 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}.
*/
- private static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT = {
+ @VisibleForTesting
+ public static final int[] NETWORK_SELECTION_DISABLE_TIMEOUT_MS = {
Integer.MAX_VALUE, // threshold for NETWORK_SELECTION_ENABLE
- 15, // threshold for DISABLED_BAD_LINK (deprecated)
- 5, // threshold for DISABLED_ASSOCIATION_REJECTION
- 5, // threshold for DISABLED_AUTHENTICATION_FAILURE
- 5, // threshold for DISABLED_DHCP_FAILURE
- 5, // threshold for DISABLED_DNS_FAILURE
+ 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_WIFI_MANAGER
+ Integer.MAX_VALUE // threshold for DISABLED_BY_USER_SWITCH
};
-
- public final AtomicBoolean mEnableAutoJoinWhenAssociated = new AtomicBoolean();
- public final AtomicBoolean mEnableChipWakeUpWhenAssociated = new AtomicBoolean(true);
- 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();
-
- public boolean mEnableLinkDebouncing;
- public boolean mEnableWifiCellularHandoverUserTriggeredAdjustment;
- public int mNetworkSwitchingBlackListPeriodMs;
- public int mBadLinkSpeed24;
- public int mBadLinkSpeed5;
- public int mGoodLinkSpeed24;
- public int mGoodLinkSpeed5;
-
- // These fields are non-final for testing.
- public AtomicInteger mThresholdQualifiedRssi5 = new AtomicInteger();
- public AtomicInteger mThresholdMinimumRssi5 = new AtomicInteger();
- public AtomicInteger mThresholdSaturatedRssi24 = new AtomicInteger();
- public AtomicInteger mThresholdMinimumRssi24 = new AtomicInteger();
- public AtomicInteger mCurrentNetworkBoost = new AtomicInteger();
- public AtomicInteger mBandAward5Ghz = new AtomicInteger();
-
- // Indicates whether the system is capable of 802.11r fast BSS transition.
- private boolean mSystemSupportsFastBssTransition = false;
-
/**
- * Framework keeps a list of ephemeral SSIDs that where deleted by user,
- * so as, framework knows not to autojoin 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.
+ * Interface for other modules to listen to the saved network updated
+ * events.
*/
- public Set<String> mDeletedEphemeralSSIDs = new HashSet<String>();
-
- /* configured networks with network id as the key */
- private final ConfigurationMap mConfiguredNetworks;
-
- /*
- * Stores whether carrier networks are configured.
- * This information is provided externally from the CarrierConfig.
- */
- private boolean mHasCarrierConfiguredNetworks;
-
- private final LocalLog mLocalLog;
- private final KeyStore mKeyStore;
- private final WifiNetworkHistory mWifiNetworkHistory;
- private final WifiConfigStore mWifiConfigStore;
- private final AnqpCache mAnqpCache;
- private final SupplicantBridge mSupplicantBridge;
- private final SupplicantBridgeCallbacks mSupplicantBridgeCallbacks;
- private final PasspointManagementObjectManager mMOManager;
- private final boolean mEnableOsuQueries;
- private final SIMAccessor mSIMAccessor;
- private final UserManager mUserManager;
- private final Object mActiveScanDetailLock = new Object();
-
- 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 */
- private ConcurrentHashMap<Integer, ScanDetailCache> mScanDetailCaches;
-
- /* Tracks the highest priority of configured networks */
- private int mLastPriority = -1;
-
- /**
- * The mLastSelectedConfiguration is used to remember which network
- * was selected last by the user.
- * The connection to this network may not be successful, as well
- * the selection (i.e. network priority) might not be persisted.
- * WiFi state machine is the only object that sets this variable.
- */
- private String mLastSelectedConfiguration = null;
- private long mLastSelectedTimeStamp =
- WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
-
- /*
- * Lost config list, whenever we read a config from networkHistory.txt that was not in
- * wpa_supplicant.conf
- */
- private HashSet<String> mLostConfigsDbg = new HashSet<String>();
-
- private ScanDetail mActiveScanDetail; // ScanDetail associated with active network
-
- private class SupplicantBridgeCallbacks implements SupplicantBridge.SupplicantBridgeCallbacks {
- @Override
- public void notifyANQPResponse(ScanDetail scanDetail,
- Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
- updateAnqpCache(scanDetail, anqpElements);
- if (anqpElements == null || anqpElements.isEmpty()) {
- return;
- }
- scanDetail.propagateANQPInfo(anqpElements);
-
- Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, false);
- Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID() + " pass 2 matches: "
- + toMatchString(matches));
-
- cacheScanResultForPasspointConfigs(scanDetail, matches, null);
- }
- @Override
- public void notifyIconFailed(long bssid) {
- Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, bssid);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- }
-
- WifiConfigManager(Context context, WifiNative wifiNative, FrameworkFacade facade, Clock clock,
- UserManager userManager, KeyStore keyStore) {
- mContext = context;
- mFacade = facade;
- mClock = clock;
- mKeyStore = keyStore;
- mUserManager = userManager;
-
- if (mShowNetworks) {
- mLocalLog = wifiNative.getLocalLog();
- } else {
- mLocalLog = null;
- }
-
- mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
- R.bool.config_wifi_only_link_same_credential_configurations);
- mMaxNumActiveChannelsForPartialScans.set(mContext.getResources().getInteger(
- R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels));
- mEnableLinkDebouncing = mContext.getResources().getBoolean(
- R.bool.config_wifi_enable_disconnection_debounce);
- mBandAward5Ghz.set(mContext.getResources().getInteger(
- R.integer.config_wifi_framework_5GHz_preference_boost_factor));
- mThresholdMinimumRssi5.set(mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz));
- mThresholdQualifiedRssi5.set(mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz));
- mThresholdSaturatedRssi5.set(mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz));
- mThresholdMinimumRssi24.set(mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz));
- mThresholdQualifiedRssi24.set(mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz));
- mThresholdSaturatedRssi24.set(mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz));
- mEnableWifiCellularHandoverUserTriggeredAdjustment = mContext.getResources().getBoolean(
- R.bool.config_wifi_framework_cellular_handover_enable_user_triggered_adjustment);
- mBadLinkSpeed24 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_link_speed_24);
- mBadLinkSpeed5 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_bad_link_speed_5);
- mGoodLinkSpeed24 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_link_speed_24);
- mGoodLinkSpeed5 = mContext.getResources().getInteger(
- R.integer.config_wifi_framework_wifi_score_good_link_speed_5);
- mEnableAutoJoinWhenAssociated.set(mContext.getResources().getBoolean(
- R.bool.config_wifi_framework_enable_associated_network_selection));
- mCurrentNetworkBoost.set(mContext.getResources().getInteger(
- R.integer.config_wifi_framework_current_network_boost));
- mNetworkSwitchingBlackListPeriodMs = mContext.getResources().getInteger(
- R.integer.config_wifi_network_switching_blacklist_time);
- mSystemSupportsFastBssTransition = mContext.getResources().getBoolean(
- R.bool.config_wifi_fast_bss_transition_enabled);
-
- boolean hs2on = mContext.getResources().getBoolean(R.bool.config_wifi_hotspot2_enabled);
- Log.d(Utils.hs2LogTag(getClass()), "Passpoint is " + (hs2on ? "enabled" : "disabled"));
-
- mConfiguredNetworks = new ConfigurationMap(userManager);
- mMOManager = new PasspointManagementObjectManager(new File(PPS_FILE), hs2on);
- mEnableOsuQueries = true;
- mAnqpCache = new AnqpCache(mClock);
- mSupplicantBridgeCallbacks = new SupplicantBridgeCallbacks();
- mSupplicantBridge = new SupplicantBridge(wifiNative, mSupplicantBridgeCallbacks);
- mScanDetailCaches = new ConcurrentHashMap<>(16, 0.75f, 2);
- 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);
- }
-
- public void trimANQPCache(boolean all) {
- mAnqpCache.clear(all, DBG);
- }
-
- void enableVerboseLogging(int verbose) {
- mEnableVerboseLogging.set(verbose);
- if (verbose > 0) {
- sVDBG = true;
- mShowNetworks = true;
- } else {
- sVDBG = false;
- }
- if (verbose > 1) {
- sVVDBG = true;
- } else {
- sVVDBG = false;
- }
- }
-
- /**
- * Fetch the list of configured networks
- * and enable all stored networks in supplicant.
- */
- void loadAndEnableAllNetworks() {
- if (DBG) log("Loading config and enabling all networks ");
- loadConfiguredNetworks();
- enableAllNetworks();
- }
-
- int getConfiguredNetworksSize() {
- return mConfiguredNetworks.sizeForCurrentUser();
- }
-
- /**
- * Fetch the list of currently saved networks (i.e. all configured networks, excluding
- * ephemeral networks).
- * @param pskMap Map of preSharedKeys, keyed by the configKey of the configuration the
- * preSharedKeys belong to
- * @return List of networks
- */
- private List<WifiConfiguration> getSavedNetworks(Map<String, String> pskMap) {
- List<WifiConfiguration> networks = new ArrayList<>();
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- WifiConfiguration newConfig = new WifiConfiguration(config);
- // When updating this condition, update WifiStateMachine's CONNECT_NETWORK handler to
- // correctly handle updating existing configs that are filtered out here.
- 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;
- }
-
- if (pskMap != null && config.allowedKeyManagement != null
- && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
- && pskMap.containsKey(config.configKey(true))) {
- newConfig.preSharedKey = pskMap.get(config.configKey(true));
- }
- networks.add(newConfig);
- }
- return networks;
- }
-
- /**
- * This function returns all configuration, and is used for debug and creating bug reports.
- */
- private List<WifiConfiguration> getAllConfiguredNetworks() {
- List<WifiConfiguration> networks = new ArrayList<>();
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- WifiConfiguration newConfig = new WifiConfiguration(config);
- networks.add(newConfig);
- }
- return networks;
- }
-
- /**
- * Fetch the list of currently saved networks (i.e. all configured networks, excluding
- * ephemeral networks).
- * @return List of networks
- */
- public List<WifiConfiguration> getSavedNetworks() {
- return getSavedNetworks(null);
- }
-
- /**
- * Check if Carrier networks have ben configured.
- * @return true if carrier networks are present else false.
- */
- public boolean hasCarrierNetworks() {
- return mHasCarrierConfiguredNetworks;
- }
-
- /**
- * Set true/false depending on whether Carrier networks have been configured.
- * @param hasCarrierNetworks if Carrier networks have been configured.
- */
- public void setHasCarrierNetworks(boolean hasCarrierNetworks) {
- mHasCarrierConfiguredNetworks = hasCarrierNetworks;
- }
-
- /**
- * Fetch the list of currently saved networks (i.e. all configured networks, excluding
- * ephemeral networks), filled with real preSharedKeys.
- * @return List of networks
- */
- List<WifiConfiguration> getPrivilegedSavedNetworks() {
- Map<String, String> pskMap = getCredentialsByConfigKeyMap();
- List<WifiConfiguration> configurations = getSavedNetworks(pskMap);
- for (WifiConfiguration configuration : configurations) {
- try {
- configuration
- .setPasspointManagementObjectTree(mMOManager.getMOTree(configuration.FQDN));
- } catch (IOException ioe) {
- Log.w(TAG, "Failed to parse MO from " + configuration.FQDN + ": " + ioe);
- }
- }
- return configurations;
- }
-
- /**
- * Fetch the list of networkId's which are hidden in current user's configuration.
- * @return List of networkIds
- */
- public Set<Integer> getHiddenConfiguredNetworkIds() {
- return mConfiguredNetworks.getHiddenNetworkIdsForCurrentUser();
- }
-
- /**
- * Find matching network for this scanResult
- */
- WifiConfiguration getMatchingConfig(ScanResult scanResult) {
- if (scanResult == null) {
- return null;
- }
- for (Map.Entry entry : mScanDetailCaches.entrySet()) {
- Integer netId = (Integer) entry.getKey();
- ScanDetailCache cache = (ScanDetailCache) entry.getValue();
- WifiConfiguration config = getWifiConfiguration(netId);
- if (config == null) {
- continue;
- }
- if (cache.get(scanResult.BSSID) != null) {
- return config;
- }
- }
-
- return null;
- }
-
- /**
- * Fetch the preSharedKeys for all networks.
- * @return a map from configKey to preSharedKey.
- */
- private Map<String, String> getCredentialsByConfigKeyMap() {
- return readNetworkVariablesFromSupplicantFile("psk");
- }
-
- /**
- * 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) {
- WifiConfiguration config = getWifiConfiguration(info.getNetworkId());
- if (config != null && getScanDetailCache(config) != null) {
- ScanDetail scanDetail = getScanDetailCache(config).getScanDetail(info.getBSSID());
- if (scanDetail != null) {
- ScanResult result = scanDetail.getScanResult();
- long previousSeen = result.seen;
- int previousRssi = result.level;
-
- // Update the scan result
- scanDetail.setSeen();
- result.level = info.getRssi();
-
- // Average the RSSI value
- result.averageRssi(previousRssi, previousSeen,
- WifiQualifiedNetworkSelector.SCAN_RESULT_MAXIMUNM_AGE);
- if (sVDBG) {
- logd("updateConfiguration freq=" + result.frequency
- + " BSSID=" + result.BSSID
- + " RSSI=" + result.level
- + " " + config.configKey());
- }
- }
- }
- }
-
- /**
- * get the Wificonfiguration for this netId
- *
- * @return Wificonfiguration
- */
- public WifiConfiguration getWifiConfiguration(int netId) {
- return mConfiguredNetworks.getForCurrentUser(netId);
- }
-
- /**
- * Get the Wificonfiguration for this key
- * @return Wificonfiguration
- */
- public WifiConfiguration getWifiConfiguration(String key) {
- return mConfiguredNetworks.getByConfigKeyForCurrentUser(key);
- }
-
- /**
- * Enable all networks (if disabled time expire) and save config. This will be a no-op if the
- * list of configured networks indicates all networks as being enabled
- */
- void enableAllNetworks() {
- boolean networkEnabledStateChanged = false;
-
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- if (config != null && !config.ephemeral
- && !config.getNetworkSelectionStatus().isNetworkEnabled()) {
- if (tryEnableQualifiedNetwork(config)) {
- networkEnabledStateChanged = true;
- }
- }
- }
-
- if (networkEnabledStateChanged) {
- saveConfig();
- sendConfiguredNetworksChangedBroadcast();
- }
- }
-
- private boolean setNetworkPriorityNative(WifiConfiguration config, int priority) {
- return mWifiConfigStore.setNetworkPriority(config, priority);
- }
-
- private boolean setSSIDNative(WifiConfiguration config, String ssid) {
- return mWifiConfigStore.setNetworkSSID(config, ssid);
- }
-
- public boolean updateLastConnectUid(WifiConfiguration config, int uid) {
- if (config != null) {
- if (config.lastConnectUid != uid) {
- config.lastConnectUid = uid;
- return true;
- }
- }
- return false;
- }
-
- /**
- * Selects the specified network for connection. This involves
- * updating the priority of all the networks and enabling the given
- * network while disabling others.
- *
- * Selecting a network will leave the other networks disabled and
- * a call to enableAllNetworks() needs to be issued upon a connection
- * or a failure event from supplicant
- *
- * @param config network to select for connection
- * @param updatePriorities makes config highest priority network
- * @return false if the network id is invalid
- */
- boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) {
- if (sVDBG) localLogNetwork("selectNetwork", config.networkId);
- if (config.networkId == INVALID_NETWORK_ID) return false;
- if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
- mUserManager.getProfiles(mCurrentUserId))) {
- loge("selectNetwork " + Integer.toString(config.networkId) + ": Network config is not "
- + "visible to current user.");
- return false;
- }
-
- // Reset the priority of each network at start or if it goes too high.
- if (mLastPriority == -1 || mLastPriority > 1000000) {
- if (updatePriorities) {
- for (WifiConfiguration config2 : mConfiguredNetworks.valuesForCurrentUser()) {
- if (config2.networkId != INVALID_NETWORK_ID) {
- setNetworkPriorityNative(config2, 0);
- }
- }
- }
- mLastPriority = 0;
- }
-
- // Set to the highest priority and save the configuration.
- if (updatePriorities) {
- setNetworkPriorityNative(config, ++mLastPriority);
- }
-
- 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
- // selectQualifiedNetwork.selectQualifiedNetwork(), when the qualified network selected
- // is a Passpoint network.
- logd("Setting SSID for WPA supplicant network " + config.networkId + " to "
- + config.SSID);
- setSSIDNative(config, config.SSID);
- }
-
- mWifiConfigStore.enableHS20(config.isPasspoint());
-
- if (updatePriorities) {
- saveConfig();
- }
-
- updateLastConnectUid(config, uid);
-
- writeKnownNetworkHistory();
-
- /* Enable the given network while disabling all other networks */
- selectNetworkWithoutBroadcast(config.networkId);
-
- /* Avoid saving the config & sending a broadcast to prevent settings
- * from displaying a disabled list of networks */
- return true;
- }
-
- /**
- * Add/update the specified configuration and save config
- *
- * @param config WifiConfiguration to be saved
- * @return network update result
- */
- NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) {
- WifiConfiguration conf;
-
- // A new network cannot have null SSID
- if (config == null || (config.networkId == INVALID_NETWORK_ID && config.SSID == null)) {
- return new NetworkUpdateResult(INVALID_NETWORK_ID);
- }
-
- if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
- mUserManager.getProfiles(mCurrentUserId))) {
- return new NetworkUpdateResult(INVALID_NETWORK_ID);
- }
-
- if (sVDBG) localLogNetwork("WifiConfigManager: saveNetwork netId", config.networkId);
- if (sVDBG) {
- logd("WifiConfigManager saveNetwork,"
- + " size=" + Integer.toString(mConfiguredNetworks.sizeForAllUsers())
- + " (for all users)"
- + " SSID=" + config.SSID
- + " Uid=" + Integer.toString(config.creatorUid)
- + "/" + Integer.toString(config.lastUpdateUid));
- }
-
- if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
- if (sVDBG) {
- logd("WifiConfigManager: removed from ephemeral blacklist: " + config.SSID);
- }
- // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call
- // below, since we're creating/modifying a config.
- }
-
- boolean newNetwork = (config.networkId == INVALID_NETWORK_ID);
- NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
- int netId = result.getNetworkId();
-
- if (sVDBG) 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);
-
- // reenable autojoin, since new information has been provided
- updateNetworkSelectionStatus(netId,
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
- }
- if (sVDBG) {
- logd("WifiConfigManager: saveNetwork got config back netId="
- + Integer.toString(netId)
- + " uid=" + Integer.toString(config.creatorUid));
- }
- }
-
- saveConfig();
- sendConfiguredNetworksChangedBroadcast(
- conf,
- result.isNewNetwork()
- ? WifiManager.CHANGE_REASON_ADDED
- : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
- return result;
- }
-
- void noteRoamingFailure(WifiConfiguration config, int reason) {
- if (config == null) return;
- config.lastRoamingFailure = mClock.currentTimeMillis();
- config.roamingFailureBlackListTimeMilli =
- 2 * (config.roamingFailureBlackListTimeMilli + 1000);
- if (config.roamingFailureBlackListTimeMilli > mNetworkSwitchingBlackListPeriodMs) {
- config.roamingFailureBlackListTimeMilli = mNetworkSwitchingBlackListPeriodMs;
- }
- config.lastRoamingFailureReason = reason;
- }
-
- void saveWifiConfigBSSID(WifiConfiguration config, String bssid) {
- mWifiConfigStore.setNetworkBSSID(config, bssid);
- }
-
-
- void updateStatus(int netId, DetailedState state) {
- if (netId != INVALID_NETWORK_ID) {
- WifiConfiguration config = mConfiguredNetworks.getForAllUsers(netId);
- if (config == null) return;
- switch (state) {
- case CONNECTED:
- config.status = Status.CURRENT;
- //we successfully connected, hence remove the blacklist
- updateNetworkSelectionStatus(netId,
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
- break;
- case DISCONNECTED:
- //If network is already disabled, keep the status
- if (config.status == Status.CURRENT) {
- config.status = Status.ENABLED;
- }
- break;
- default:
- //do nothing, retain the existing state
- break;
- }
- }
- }
-
-
- /**
- * Disable an ephemeral SSID for the purpose of auto-joining thru scored.
- * This SSID will never be scored anymore.
- * The only way to "un-disable it" is if the user create a network for that SSID and then
- * forget it.
- *
- * @param ssid caller must ensure that the SSID passed thru this API match
- * the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
- * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
- * disconnect if this is the current network.
- */
- WifiConfiguration disableEphemeralNetwork(String ssid) {
- if (ssid == null) {
- return null;
- }
-
- WifiConfiguration foundConfig = mConfiguredNetworks.getEphemeralForCurrentUser(ssid);
-
- mDeletedEphemeralSSIDs.add(ssid);
- logd("Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size());
-
- if (foundConfig != null) {
- logd("Found ephemeral config in disableEphemeralNetwork: " + foundConfig.networkId);
- }
-
- writeKnownNetworkHistory();
- return foundConfig;
- }
-
- /**
- * Forget the specified network and save config
- *
- * @param netId network to forget
- * @return {@code true} if it succeeds, {@code false} otherwise
- */
- boolean forgetNetwork(int netId) {
- if (mShowNetworks) localLogNetwork("forgetNetwork", netId);
- if (!removeNetwork(netId)) {
- loge("Failed to forget network " + netId);
- return false;
- }
- saveConfig();
- writeKnownNetworkHistory();
- return true;
- }
-
- /**
- * Add/update a network. Note that there is no saveConfig operation.
- * This function is retained for compatibility with the public
- * API. The more powerful saveNetwork() is used by the
- * state machine
- *
- * @param config wifi configuration to add/update
- * @return network Id
- */
- int addOrUpdateNetwork(WifiConfiguration config, int uid) {
- if (config == null || !WifiConfigurationUtil.isVisibleToAnyProfile(config,
- mUserManager.getProfiles(mCurrentUserId))) {
- return WifiConfiguration.INVALID_NETWORK_ID;
- }
-
- if (mShowNetworks) localLogNetwork("addOrUpdateNetwork id=", config.networkId);
- if (config.isPasspoint()) {
- /* create a temporary SSID with providerFriendlyName */
- Long csum = getChecksum(config.FQDN);
- config.SSID = csum.toString();
- config.enterpriseConfig.setDomainSuffixMatch(config.FQDN);
- }
-
- NetworkUpdateResult result = addOrUpdateNetworkNative(config, uid);
- if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
- WifiConfiguration conf = mConfiguredNetworks.getForCurrentUser(result.getNetworkId());
- if (conf != null) {
- sendConfiguredNetworksChangedBroadcast(
- conf,
- result.isNewNetwork
- ? WifiManager.CHANGE_REASON_ADDED
- : WifiManager.CHANGE_REASON_CONFIG_CHANGE);
- }
- }
-
- return result.getNetworkId();
- }
-
- public int addPasspointManagementObject(String managementObject) {
- try {
- mMOManager.addSP(managementObject);
- return 0;
- } catch (IOException | SAXException e) {
- return -1;
- }
- }
-
- public int modifyPasspointMo(String fqdn, List<PasspointManagementObjectDefinition> mos) {
- try {
- return mMOManager.modifySP(fqdn, mos);
- } catch (IOException | SAXException e) {
- return -1;
- }
- }
-
- public boolean queryPasspointIcon(long bssid, String fileName) {
- return mSupplicantBridge.doIconQuery(bssid, fileName);
- }
-
- public int matchProviderWithCurrentNetwork(String fqdn) {
- ScanDetail scanDetail = null;
- synchronized (mActiveScanDetailLock) {
- scanDetail = mActiveScanDetail;
- }
- if (scanDetail == null) {
- return PasspointMatch.None.ordinal();
- }
- HomeSP homeSP = mMOManager.getHomeSP(fqdn);
- if (homeSP == null) {
- return PasspointMatch.None.ordinal();
- }
-
- ANQPData anqpData = mAnqpCache.getEntry(scanDetail.getNetworkDetail());
-
- Map<Constants.ANQPElementType, ANQPElement> anqpElements =
- anqpData != null ? anqpData.getANQPElements() : null;
-
- return homeSP.match(scanDetail.getNetworkDetail(), anqpElements, mSIMAccessor).ordinal();
- }
-
- /**
- * General PnoNetwork list sorting algorithm:
- * 1, Place the fully enabled networks first. Among the fully enabled networks,
- * sort them in the oder determined by the return of |compareConfigurations| method
- * implementation.
- * 2. Next place all the temporarily disabled networks. Among the temporarily disabled
- * networks, sort them in the order determined by the return of |compareConfigurations| method
- * implementation.
- * 3. Place the permanently disabled networks last. The order among permanently disabled
- * networks doesn't matter.
- */
- private static class PnoListComparator implements Comparator<WifiConfiguration> {
-
- public final int ENABLED_NETWORK_SCORE = 3;
- public final int TEMPORARY_DISABLED_NETWORK_SCORE = 2;
- public final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1;
-
- @Override
- public int compare(WifiConfiguration a, WifiConfiguration b) {
- int configAScore = getPnoNetworkSortScore(a);
- int configBScore = getPnoNetworkSortScore(b);
- if (configAScore == configBScore) {
- return compareConfigurations(a, b);
- } else {
- return Integer.compare(configBScore, configAScore);
- }
- }
-
- // This needs to be implemented by the connected/disconnected PNO list comparator.
- public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) {
- return 0;
- }
-
+ public interface OnSavedNetworkUpdateListener {
/**
- * Returns an integer representing a score for each configuration. The scores are assigned
- * based on the status of the configuration. The scores are assigned according to the order:
- * Fully enabled network > Temporarily disabled network > Permanently disabled network.
+ * Invoked on saved network being added.
*/
- private int getPnoNetworkSortScore(WifiConfiguration config) {
- if (config.getNetworkSelectionStatus().isNetworkEnabled()) {
- return ENABLED_NETWORK_SCORE;
- } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
- return TEMPORARY_DISABLED_NETWORK_SCORE;
- } else {
- return PERMANENTLY_DISABLED_NETWORK_SCORE;
- }
- }
+ void onSavedNetworkAdded(int networkId);
+ /**
+ * Invoked on saved network being enabled.
+ */
+ void onSavedNetworkEnabled(int networkId);
+ /**
+ * Invoked on saved network being permanently disabled.
+ */
+ void onSavedNetworkPermanentlyDisabled(int networkId);
+ /**
+ * Invoked on saved network being removed.
+ */
+ void onSavedNetworkRemoved(int networkId);
+ /**
+ * Invoked on saved network being temporarily disabled.
+ */
+ void onSavedNetworkTemporarilyDisabled(int networkId);
+ /**
+ * Invoked on saved network being updated.
+ */
+ void onSavedNetworkUpdated(int networkId);
}
-
/**
- * Disconnected PnoNetwork list sorting algorithm:
- * Place the configurations in descending order of their |numAssociation| values. If networks
- * have the same |numAssociation|, then sort them in descending order of their |priority|
- * values.
+ * Max size of scan details to cache in {@link #mScanDetailCaches}.
*/
- private static final PnoListComparator sDisconnectedPnoListComparator =
- new PnoListComparator() {
+ @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;
+ /**
+ * Link networks only if they have less than this number of scan cache entries.
+ */
+ @VisibleForTesting
+ public static final int LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES = 6;
+ /**
+ * Link networks only if the bssid in scan results for the networks match in the first
+ * 16 ASCII chars in the bssid string. For example = "af:de:56;34:15:7"
+ */
+ @VisibleForTesting
+ public static final int LINK_CONFIGURATION_BSSID_MATCH_LENGTH = 16;
+ /**
+ * 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 = "WifiConfigManager";
+ /**
+ * Maximum age of scan results that can be used for averaging out RSSI value.
+ */
+ private static final int SCAN_RESULT_MAXIMUM_AGE_MS = 40000;
+ /**
+ * General sorting algorithm of all networks for scanning purposes:
+ * Place the configurations in descending order of their |numAssociation| values. If networks
+ * have the same |numAssociation|, place the configurations with
+ * |lastSeenInQualifiedNetworkSelection| set first.
+ */
+ private static final WifiConfigurationUtil.WifiConfigurationComparator sScanListComparator =
+ new WifiConfigurationUtil.WifiConfigurationComparator() {
@Override
- public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) {
+ public int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b) {
if (a.numAssociation != b.numAssociation) {
return Long.compare(b.numAssociation, a.numAssociation);
} else {
- return Integer.compare(b.priority, a.priority);
- }
- }
- };
-
- /**
- * Retrieves an updated list of priorities for all the saved networks before
- * enabling disconnected PNO (wpa_supplicant based PNO).
- *
- * wpa_supplicant uses the priority of networks to build the list of SSID's to monitor
- * during PNO. If there are a lot of saved networks, this list will be truncated and we
- * might end up not connecting to the networks we use most frequently. So, We want the networks
- * to be re-sorted based on the relative |numAssociation| values.
- *
- * @return list of networks with updated priorities.
- */
- public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveDisconnectedPnoNetworkList() {
- return retrievePnoNetworkList(sDisconnectedPnoListComparator);
- }
-
- /**
- * Connected PnoNetwork list sorting algorithm:
- * Place the configurations with |lastSeenInQualifiedNetworkSelection| set first. If networks
- * have the same value, then sort them in descending order of their |numAssociation|
- * values.
- */
- private static final PnoListComparator sConnectedPnoListComparator =
- new PnoListComparator() {
- @Override
- public int compareConfigurations(WifiConfiguration a, WifiConfiguration b) {
- boolean isConfigALastSeen =
- a.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection();
- boolean isConfigBLastSeen =
- b.getNetworkSelectionStatus().getSeenInLastQualifiedNetworkSelection();
- if (isConfigALastSeen != isConfigBLastSeen) {
+ boolean isConfigALastSeen =
+ a.getNetworkSelectionStatus()
+ .getSeenInLastQualifiedNetworkSelection();
+ boolean isConfigBLastSeen =
+ b.getNetworkSelectionStatus()
+ .getSeenInLastQualifiedNetworkSelection();
return Boolean.compare(isConfigBLastSeen, isConfigALastSeen);
- } else {
- return Long.compare(b.numAssociation, a.numAssociation);
}
}
};
/**
- * Retrieves an updated list of priorities for all the saved networks before
- * enabling connected PNO (HAL based ePno).
- *
- * @return list of networks with updated priorities.
+ * List of external dependencies for WifiConfigManager.
*/
- public ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrieveConnectedPnoNetworkList() {
- return retrievePnoNetworkList(sConnectedPnoListComparator);
+ private final Context mContext;
+ private final Clock mClock;
+ private final UserManager mUserManager;
+ private final BackupManagerProxy mBackupManagerProxy;
+ private final TelephonyManager mTelephonyManager;
+ private final WifiKeyStore mWifiKeyStore;
+ private final WifiConfigStore mWifiConfigStore;
+ private final WifiConfigStoreLegacy mWifiConfigStoreLegacy;
+ private final WifiPermissionsUtil mWifiPermissionsUtil;
+ private final WifiPermissionsWrapper mWifiPermissionsWrapper;
+ /**
+ * 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 Map<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;
+ /**
+ * Flag to indicate if only networks with the same psk should be linked.
+ * TODO(b/30706406): Remove this flag if unused.
+ */
+ private final boolean mOnlyLinkSameCredentialConfigurations;
+ /**
+ * Number of channels to scan for during partial scans initiated while connected.
+ */
+ private final int mMaxNumActiveChannelsForPartialScans;
+ /**
+ * Verbose logging flag. Toggled by developer options.
+ */
+ private boolean mVerboseLoggingEnabled = false;
+ /**
+ * Current logged in user ID.
+ */
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
+ /**
+ * Flag to indicate that the new user's store has not yet been read since user switch.
+ * Initialize this flag to |true| to trigger a read on the first user unlock after
+ * bootup.
+ */
+ private boolean mPendingUnlockStoreRead = true;
+ /**
+ * Flag to indicate if we have performed a read from store at all. This is used to gate
+ * any user unlock/switch operations until we read the store (Will happen if wifi is disabled
+ * when user updates from N to O).
+ */
+ private boolean mPendingStoreRead = true;
+ /**
+ * Flag to indicate if the user unlock was deferred until the store load occurs.
+ */
+ private boolean mDeferredUserUnlockRead = false;
+ /**
+ * This is keeping track of the next network ID to be assigned. Any new networks will be
+ * assigned |mNextNetworkId| as network ID.
+ */
+ private int mNextNetworkId = 0;
+ /**
+ * UID of system UI. This uid is allowed to modify network configurations regardless of which
+ * user is logged in.
+ */
+ private int mSystemUiUid = -1;
+ /**
+ * This is used to remember which network was selected successfully last by an app. This is set
+ * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
+ * This is the only way for an app to request connection to a specific network using the
+ * {@link WifiManager} API's.
+ */
+ private int mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
+ private long mLastSelectedTimeStamp =
+ WifiConfiguration.NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
+
+ // Store data for network list and deleted ephemeral SSID list. Used for serializing
+ // parsing data to/from the config store.
+ private final NetworkListStoreData mNetworkListStoreData;
+ private final DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData;
+
+ // Store the saved network update listener.
+ private OnSavedNetworkUpdateListener mListener = null;
+
+ /**
+ * Create new instance of WifiConfigManager.
+ */
+ WifiConfigManager(
+ Context context, Clock clock, UserManager userManager,
+ TelephonyManager telephonyManager, WifiKeyStore wifiKeyStore,
+ WifiConfigStore wifiConfigStore, WifiConfigStoreLegacy wifiConfigStoreLegacy,
+ WifiPermissionsUtil wifiPermissionsUtil,
+ WifiPermissionsWrapper wifiPermissionsWrapper,
+ NetworkListStoreData networkListStoreData,
+ DeletedEphemeralSsidsStoreData deletedEphemeralSsidsStoreData) {
+ mContext = context;
+ mClock = clock;
+ mUserManager = userManager;
+ mBackupManagerProxy = new BackupManagerProxy();
+ mTelephonyManager = telephonyManager;
+ mWifiKeyStore = wifiKeyStore;
+ mWifiConfigStore = wifiConfigStore;
+ mWifiConfigStoreLegacy = wifiConfigStoreLegacy;
+ mWifiPermissionsUtil = wifiPermissionsUtil;
+ mWifiPermissionsWrapper = wifiPermissionsWrapper;
+
+ mConfiguredNetworks = new ConfigurationMap(userManager);
+ mScanDetailCaches = new HashMap<>(16, 0.75f);
+ mDeletedEphemeralSSIDs = new HashSet<>();
+
+ // Register store data for network list and deleted ephemeral SSIDs.
+ mNetworkListStoreData = networkListStoreData;
+ mDeletedEphemeralSsidsStoreData = deletedEphemeralSsidsStoreData;
+ mWifiConfigStore.registerStoreData(mNetworkListStoreData);
+ mWifiConfigStore.registerStoreData(mDeletedEphemeralSsidsStoreData);
+
+ mOnlyLinkSameCredentialConfigurations = mContext.getResources().getBoolean(
+ R.bool.config_wifi_only_link_same_credential_configurations);
+ mMaxNumActiveChannelsForPartialScans = mContext.getResources().getInteger(
+ R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels);
+
+ try {
+ mSystemUiUid = mContext.getPackageManager().getPackageUidAsUser(SYSUI_PACKAGE_NAME,
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to resolve SystemUI's UID.");
+ }
}
/**
- * Create a PnoNetwork object from the provided WifiConfiguration.
- * @param config Configuration corresponding to the network.
- * @param newPriority New priority to be assigned to the network.
+ * 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.
*/
- private static WifiScanner.PnoSettings.PnoNetwork createPnoNetworkFromWifiConfiguration(
- WifiConfiguration config, int newPriority) {
- WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
- new WifiScanner.PnoSettings.PnoNetwork(config.SSID);
- pnoNetwork.networkId = config.networkId;
- pnoNetwork.priority = newPriority;
- if (config.hiddenSSID) {
- pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN;
- }
- pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND;
- pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND;
- if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
- pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK;
- } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
- || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
- pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL;
+ @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 {
- pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN;
+ mVerboseLoggingEnabled = false;
}
- return pnoNetwork;
+ mWifiConfigStore.enableVerboseLogging(mVerboseLoggingEnabled);
+ mWifiKeyStore.enableVerboseLogging(mVerboseLoggingEnabled);
}
/**
- * Retrieves an updated list of priorities for all the saved networks before
- * enabling/disabling PNO.
- *
- * @param pnoListComparator The comparator to use for sorting networks
- * @return list of networks with updated priorities.
+ * 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 ArrayList<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList(
- PnoListComparator pnoListComparator) {
- ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
- ArrayList<WifiConfiguration> wifiConfigurations =
- new ArrayList<>(mConfiguredNetworks.valuesForCurrentUser());
- // Remove any permanently disabled networks.
- Iterator<WifiConfiguration> iter = wifiConfigurations.iterator();
- while (iter.hasNext()) {
- WifiConfiguration config = iter.next();
- if (config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
- iter.remove();
+ 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;
+ }
}
}
- Collections.sort(wifiConfigurations, pnoListComparator);
- // Let's use the network list size as the highest priority and then go down from there.
- // So, the most frequently connected network has the highest priority now.
- int priority = wifiConfigurations.size();
- for (WifiConfiguration config : wifiConfigurations) {
- pnoList.add(createPnoNetworkFromWifiConfiguration(config, priority));
- priority--;
+ if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) {
+ configuration.enterpriseConfig.setPassword(PASSWORD_MASK);
}
- return pnoList;
}
/**
- * Remove a network. Note that there is no saveConfig operation.
- * This function is retained for compatibility with the public
- * API. The more powerful forgetNetwork() is used by the
- * state machine for network removal
+ * Helper method to create a copy of the provided internal WifiConfiguration object to be
+ * passed to external modules.
*
- * @param netId network to be removed
- * @return {@code true} if it succeeds, {@code false} otherwise
+ * @param configuration provided WifiConfiguration object.
+ * @param maskPasswords Mask passwords or not.
+ * @return Copy of the WifiConfiguration object.
*/
- boolean removeNetwork(int netId) {
- if (mShowNetworks) localLogNetwork("removeNetwork", netId);
- WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
- if (!removeConfigAndSendBroadcastIfNeeded(config)) {
+ private WifiConfiguration createExternalWifiConfiguration(
+ WifiConfiguration configuration, boolean maskPasswords) {
+ WifiConfiguration network = new WifiConfiguration(configuration);
+ if (maskPasswords) {
+ maskPasswordsInWifiConfiguration(network);
+ }
+ return network;
+ }
+
+ /**
+ * 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;
+ }
+ networks.add(createExternalWifiConfiguration(config, maskPasswords));
+ }
+ 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.
+ return createExternalWifiConfiguration(config, true);
+ }
+
+ /**
+ * Retrieves the configured network corresponding to the provided config key with password
+ * masked.
+ *
+ * @param configKey configKey of the requested network.
+ * @return WifiConfiguration object if found, null otherwise.
+ */
+ public WifiConfiguration getConfiguredNetwork(String configKey) {
+ WifiConfiguration config = getInternalConfiguredNetwork(configKey);
+ if (config == null) {
+ return null;
+ }
+ // Create a new configuration object with the passwords masked to send out to the external
+ // world.
+ return createExternalWifiConfiguration(config, true);
+ }
+
+ /**
+ * 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.
+ return createExternalWifiConfiguration(config, false);
+ }
+
+ /**
+ * 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;
+ }
+ internalConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
+ if (internalConfig == null) {
+ Log.e(TAG, "Cannot find network with networkId " + config.networkId
+ + " or configKey " + config.configKey());
+ }
+ return internalConfig;
+ }
+
+ /**
+ * Helper method to retrieve the internal WifiConfiguration object corresponding to the
+ * provided network ID in our database.
+ */
+ private WifiConfiguration getInternalConfiguredNetwork(int networkId) {
+ if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
+ Log.w(TAG, "Looking up network with invalid networkId -1");
+ return null;
+ }
+ WifiConfiguration internalConfig = mConfiguredNetworks.getForCurrentUser(networkId);
+ if (internalConfig == null) {
+ Log.e(TAG, "Cannot find network with networkId " + networkId);
+ }
+ return internalConfig;
+ }
+
+ /**
+ * Helper method to retrieve the internal WifiConfiguration object corresponding to the
+ * provided configKey in our database.
+ */
+ private WifiConfiguration getInternalConfiguredNetwork(String configKey) {
+ WifiConfiguration internalConfig =
+ mConfiguredNetworks.getByConfigKeyForCurrentUser(configKey);
+ if (internalConfig == null) {
+ Log.e(TAG, "Cannot find network with configKey " + configKey);
+ }
+ return internalConfig;
+ }
+
+ /**
+ * 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 |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 connection attempts.
+ */
+ private boolean canModifyNetwork(WifiConfiguration config, int uid, boolean ignoreLockdown) {
+ // Passpoint configurations are generated and managed by PasspointManager. They can be
+ // added by either PasspointNetworkEvaluator (for auto connection) or Settings app
+ // (for manual connection), and need to be removed once the connection is completed.
+ // Since it is "owned" by us, so always allow us to modify them.
+ if (config.isPasspoint() && uid == Process.WIFI_UID) {
+ return true;
+ }
+
+ // EAP-SIM/AKA/AKA' network needs framework to update the anonymous identity provided
+ // by authenticator back to the WifiConfiguration object.
+ // Since it is "owned" by us, so always allow us to modify them.
+ if (config.enterpriseConfig != null
+ && uid == Process.WIFI_UID
+ && TelephonyUtil.isSimEapMethod(config.enterpriseConfig.getEapMethod())) {
+ return true;
+ }
+
+ 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| holds the |OVERRIDE_CONFIG_WIFI| permission if the caller asks us to
+ // bypass the lockdown checks.
+ if (ignoreLockdown) {
+ return mWifiPermissionsUtil.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;
}
- if (config.isPasspoint()) {
- writePasspointConfigs(config.FQDN, null);
+
+ // 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 || mWifiPermissionsUtil.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 && mWifiPermissionsUtil.checkConfigOverridePermission(uid);
+ }
+
+ /**
+ * Method to check if the provided UID belongs to the current foreground user or some other
+ * app (only SysUI today) running on behalf of the user.
+ * This is used to prevent any background user apps from modifying network configurations.
+ *
+ * @param uid uid of the app.
+ * @return true if the UID belongs to the current foreground app or SystemUI, false otherwise.
+ */
+ private boolean doesUidBelongToCurrentUser(int uid) {
+ return (WifiConfigurationUtil.doesUidBelongToAnyProfile(
+ uid, mUserManager.getProfiles(mCurrentUserId)) || (uid == mSystemUiUid));
+ }
+
+ /**
+ * 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 internalConfig WifiConfiguration object in our internal map.
+ * @param externalConfig WifiConfiguration object provided from the external API.
+ */
+ private void mergeWithInternalWifiConfiguration(
+ WifiConfiguration internalConfig, WifiConfiguration externalConfig) {
+ if (externalConfig.SSID != null) {
+ internalConfig.SSID = externalConfig.SSID;
+ }
+ if (externalConfig.BSSID != null) {
+ internalConfig.BSSID = externalConfig.BSSID.toLowerCase();
+ }
+ internalConfig.hiddenSSID = externalConfig.hiddenSSID;
+ if (externalConfig.preSharedKey != null
+ && !externalConfig.preSharedKey.equals(PASSWORD_MASK)) {
+ 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
+ && !externalConfig.wepKeys[i].equals(PASSWORD_MASK)) {
+ 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.clone();
+ }
+
+ // Copy over all the auth/protocol/key mgmt parameters if set.
+ if (externalConfig.allowedAuthAlgorithms != null
+ && !externalConfig.allowedAuthAlgorithms.isEmpty()) {
+ internalConfig.allowedAuthAlgorithms =
+ (BitSet) externalConfig.allowedAuthAlgorithms.clone();
+ }
+ if (externalConfig.allowedProtocols != null
+ && !externalConfig.allowedProtocols.isEmpty()) {
+ internalConfig.allowedProtocols = (BitSet) externalConfig.allowedProtocols.clone();
+ }
+ if (externalConfig.allowedKeyManagement != null
+ && !externalConfig.allowedKeyManagement.isEmpty()) {
+ internalConfig.allowedKeyManagement =
+ (BitSet) externalConfig.allowedKeyManagement.clone();
+ }
+ if (externalConfig.allowedPairwiseCiphers != null
+ && !externalConfig.allowedPairwiseCiphers.isEmpty()) {
+ internalConfig.allowedPairwiseCiphers =
+ (BitSet) externalConfig.allowedPairwiseCiphers.clone();
+ }
+ if (externalConfig.allowedGroupCiphers != null
+ && !externalConfig.allowedGroupCiphers.isEmpty()) {
+ internalConfig.allowedGroupCiphers =
+ (BitSet) externalConfig.allowedGroupCiphers.clone();
+ }
+
+ // Copy over the |IpConfiguration| parameters if set.
+ if (externalConfig.getIpConfiguration() != null) {
+ IpConfiguration.IpAssignment ipAssignment = externalConfig.getIpAssignment();
+ if (ipAssignment != IpConfiguration.IpAssignment.UNASSIGNED) {
+ internalConfig.setIpAssignment(ipAssignment);
+ if (ipAssignment == IpConfiguration.IpAssignment.STATIC) {
+ internalConfig.setStaticIpConfiguration(
+ new StaticIpConfiguration(externalConfig.getStaticIpConfiguration()));
+ }
+ }
+ IpConfiguration.ProxySettings proxySettings = externalConfig.getProxySettings();
+ if (proxySettings != IpConfiguration.ProxySettings.UNASSIGNED) {
+ internalConfig.setProxySettings(proxySettings);
+ if (proxySettings == IpConfiguration.ProxySettings.PAC
+ || proxySettings == IpConfiguration.ProxySettings.STATIC) {
+ internalConfig.setHttpProxy(new ProxyInfo(externalConfig.getHttpProxy()));
+ }
+ }
+ }
+
+ // Copy over the |WifiEnterpriseConfig| parameters if set.
+ if (externalConfig.enterpriseConfig != null) {
+ internalConfig.enterpriseConfig.copyFromExternal(
+ externalConfig.enterpriseConfig, PASSWORD_MASK);
+ }
+ }
+
+ /**
+ * 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.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);
+
+ configuration.status = WifiConfiguration.Status.DISABLED;
+ configuration.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
+ }
+
+ /**
+ * Create a new internal WifiConfiguration object by copying over parameters from the provided
+ * external configuration and set defaults for the appropriate parameters.
+ *
+ * @param externalConfig WifiConfiguration object provided from the external API.
+ * @return New WifiConfiguration object with parameters merged from the provided external
+ * configuration.
+ */
+ private WifiConfiguration createNewInternalWifiConfigurationFromExternal(
+ WifiConfiguration externalConfig, int uid) {
+ WifiConfiguration newInternalConfig = new WifiConfiguration();
+
+ // First allocate a new network ID for the configuration.
+ newInternalConfig.networkId = mNextNetworkId++;
+
+ // First set defaults in the new configuration created.
+ setDefaultsInWifiConfiguration(newInternalConfig);
+
+ // Copy over all the public elements from the provided configuration.
+ mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
+
+ // 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.
+ newInternalConfig.requirePMF = externalConfig.requirePMF;
+ newInternalConfig.noInternetAccessExpected = externalConfig.noInternetAccessExpected;
+ newInternalConfig.ephemeral = externalConfig.ephemeral;
+ newInternalConfig.meteredHint = externalConfig.meteredHint;
+ newInternalConfig.useExternalScores = externalConfig.useExternalScores;
+ newInternalConfig.shared = externalConfig.shared;
+
+ // Add debug information for network addition.
+ newInternalConfig.creatorUid = newInternalConfig.lastUpdateUid = uid;
+ newInternalConfig.creatorName = newInternalConfig.lastUpdateName =
+ mContext.getPackageManager().getNameForUid(uid);
+ newInternalConfig.creationTime = newInternalConfig.updateTime =
+ createDebugTimeStampString(mClock.getWallClockMillis());
+
+ return newInternalConfig;
+ }
+
+ /**
+ * Create a new internal WifiConfiguration object by copying over parameters from the provided
+ * external configuration to a copy of the existing internal WifiConfiguration object.
+ *
+ * @param internalConfig WifiConfiguration object in our internal map.
+ * @param externalConfig WifiConfiguration object provided from the external API.
+ * @return Copy of existing WifiConfiguration object with parameters merged from the provided
+ * configuration.
+ */
+ private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal(
+ WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid) {
+ WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig);
+
+ // Copy over all the public elements from the provided configuration.
+ mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
+
+ // Add debug information for network update.
+ newInternalConfig.lastUpdateUid = uid;
+ newInternalConfig.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
+ newInternalConfig.updateTime = createDebugTimeStampString(mClock.getWallClockMillis());
+
+ return newInternalConfig;
+ }
+
+ /**
+ * 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());
+ }
+ WifiConfiguration newInternalConfig = null;
+
+ // First check if we already have a network with the provided network id or configKey.
+ WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config);
+ // No existing network found. So, potentially a network add.
+ if (existingInternalConfig == null) {
+ newInternalConfig = createNewInternalWifiConfigurationFromExternal(config, uid);
+ // Since the original config provided may have had an empty
+ // {@link WifiConfiguration#allowedKeyMgmt} field, check again if we already have a
+ // network with the the same configkey.
+ existingInternalConfig = getInternalConfiguredNetwork(newInternalConfig.configKey());
+ }
+ // Existing network found. So, a network update.
+ if (existingInternalConfig != null) {
+ // 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(
+ existingInternalConfig, config, uid);
+ }
+
+ // Only add networks with proxy settings if the user has permission to
+ if (WifiConfigurationUtil.hasProxyChanged(existingInternalConfig, newInternalConfig)
+ && !canModifyProxySettings(uid)) {
+ Log.e(TAG, "UID " + uid + " does not have permission to modify proxy Settings "
+ + config.configKey() + ". Must have OVERRIDE_WIFI_CONFIG,"
+ + " or be device or profile owner.");
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+
+ // Update the keys for non-Passpoint enterprise networks. For Passpoint, the certificates
+ // and keys are installed at the time the provider is installed.
+ if (config.enterpriseConfig != null
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE
+ && !config.isPasspoint()) {
+ if (!(mWifiKeyStore.updateNetworkKeys(newInternalConfig, existingInternalConfig))) {
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+ }
+
+ boolean newNetwork = (existingInternalConfig == null);
+ // This is needed to inform IpManager about any IP configuration changes.
+ boolean hasIpChanged =
+ newNetwork || WifiConfigurationUtil.hasIpChanged(
+ existingInternalConfig, newInternalConfig);
+ boolean hasProxyChanged =
+ newNetwork || WifiConfigurationUtil.hasProxyChanged(
+ existingInternalConfig, newInternalConfig);
+ // Reset the |hasEverConnected| flag if the credential parameters changed in this update.
+ boolean hasCredentialChanged =
+ newNetwork || WifiConfigurationUtil.hasCredentialChanged(
+ existingInternalConfig, newInternalConfig);
+ if (hasCredentialChanged) {
+ newInternalConfig.getNetworkSelectionStatus().setHasEverConnected(false);
+ }
+
+ // Add it to our internal map. This will replace any existing network configuration for
+ // updates.
+ mConfiguredNetworks.put(newInternalConfig);
+
+ if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Removed from ephemeral blacklist: " + config.SSID);
+ }
+ }
+
+ // Stage the backup of the SettingsProvider package which backs this up.
+ mBackupManagerProxy.notifyDataChanged();
+
+ NetworkUpdateResult result =
+ new NetworkUpdateResult(hasIpChanged, hasProxyChanged, hasCredentialChanged);
+ 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/modification.
+ * @return NetworkUpdateResult object representing status of the update.
+ */
+ public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) {
+ if (!doesUidBelongToCurrentUser(uid)) {
+ Log.e(TAG, "UID " + uid + " not visible to the current user");
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+ if (config == null) {
+ Log.e(TAG, "Cannot add/update network with null config");
+ return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+ if (mPendingStoreRead) {
+ Log.e(TAG, "Cannot add/update network before store is read!");
+ 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);
+ // Unless the added network is ephemeral or Passpoint, persist the network update/addition.
+ if (!config.ephemeral && !config.isPasspoint()) {
+ saveToStore(true);
+ if (mListener != null) {
+ if (result.isNewNetwork()) {
+ mListener.onSavedNetworkAdded(newConfig.networkId);
+ } else {
+ mListener.onSavedNetworkUpdated(newConfig.networkId);
+ }
+ }
+ }
+ 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 for non-Passpoint networks.
+ if (!config.isPasspoint() && config.enterpriseConfig != null
+ && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
+ mWifiKeyStore.removeKeys(config.enterpriseConfig);
+ }
+
+ removeConnectChoiceFromAllNetworks(config.configKey());
+ 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;
}
- private static Long getChecksum(String source) {
- Checksum csum = new CRC32();
- csum.update(source.getBytes(), 0, source.getBytes().length);
- return csum.getValue();
- }
-
- private boolean removeConfigWithoutBroadcast(WifiConfiguration config) {
+ /**
+ * Removes the specified network configuration from our database.
+ *
+ * @param networkId network ID of the provided network.
+ * @param uid UID of the app requesting the network deletion.
+ * @return true if successful, false otherwise.
+ */
+ public boolean removeNetwork(int networkId, int uid) {
+ if (!doesUidBelongToCurrentUser(uid)) {
+ Log.e(TAG, "UID " + uid + " not visible to the current user");
+ return false;
+ }
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
if (config == null) {
return false;
}
- if (!mWifiConfigStore.removeNetwork(config)) {
- loge("Failed to remove network " + config.networkId);
+ if (!canModifyNetwork(config, uid, DISALLOW_LOCKDOWN_CHECK_BYPASS)) {
+ Log.e(TAG, "UID " + uid + " does not have permission to delete configuration "
+ + config.configKey());
return false;
}
- if (config.configKey().equals(mLastSelectedConfiguration)) {
- mLastSelectedConfiguration = null;
+ if (!removeNetworkInternal(config)) {
+ Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
+ return false;
}
- mConfiguredNetworks.remove(config.networkId);
- mScanDetailCaches.remove(config.networkId);
+ if (networkId == mLastSelectedNetworkId) {
+ clearLastSelectedNetwork();
+ }
+ sendConfiguredNetworkChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
+ // Unless the removed network is ephemeral or Passpoint, persist the network removal.
+ if (!config.ephemeral && !config.isPasspoint()) {
+ saveToStore(true);
+ if (mListener != null) mListener.onSavedNetworkRemoved(networkId);
+ }
return true;
}
- private boolean removeConfigAndSendBroadcastIfNeeded(WifiConfiguration config) {
- if (!removeConfigWithoutBroadcast(config)) {
- return false;
- }
- String key = config.configKey();
- if (sVDBG) {
- logd("removeNetwork " + " key=" + key + " config.id=" + config.networkId);
- }
- writeIpAndProxyConfigurations();
- sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_REMOVED);
- if (!config.ephemeral) {
- removeUserSelectionPreference(key);
- }
- writeKnownNetworkHistory();
- return true;
- }
-
- private void removeUserSelectionPreference(String configKey) {
- if (DBG) {
- Log.d(TAG, "removeUserSelectionPreference: key is " + configKey);
- }
- if (configKey == null) {
- return;
- }
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
- String connectChoice = status.getConnectChoice();
- if (connectChoice != null && connectChoice.equals(configKey)) {
- Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
- + " : " + config.networkId);
- status.setConnectChoice(null);
- status.setConnectChoiceTimestamp(WifiConfiguration.NetworkSelectionStatus
- .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
- }
- }
- }
-
- /*
- * Remove all networks associated with an application
+ /**
+ * Remove all networks associated with an application.
*
* @param app Application info of the package of networks to remove.
* @return the {@link Set} of networks that were removed by this call. Networks which matched
@@ -1262,7 +1145,6 @@
if (app == null || app.packageName == null) {
return Collections.<Integer>emptySet();
}
-
Log.d(TAG, "Remove all networks for app " + app);
Set<Integer> removedNetworks = new ArraySet<>();
WifiConfiguration[] copiedConfigs =
@@ -1274,11 +1156,10 @@
localLog("Removing network " + config.SSID
+ ", application \"" + app.packageName + "\" uninstalled"
+ " from user " + UserHandle.getUserId(app.uid));
- if (removeNetwork(config.networkId)) {
+ if (removeNetwork(config.networkId, mSystemUiUid)) {
removedNetworks.add(config.networkId);
}
}
- saveConfig();
return removedNetworks;
}
@@ -1299,7 +1180,7 @@
continue;
}
localLog("Removing network " + config.SSID + ", user " + userId + " removed");
- if (removeNetwork(config.networkId)) {
+ if (removeNetwork(config.networkId, mSystemUiUid)) {
removedNetworks.add(config.networkId);
}
}
@@ -1307,1336 +1188,677 @@
}
/**
- * Enable a network. Note that there is no saveConfig operation.
- * This function is retained for compatibility with the public
- * API. The more powerful selectNetwork()/saveNetwork() is used by the
- * state machine for connecting to a network
- *
- * @param config network to be enabled
- * @return {@code true} if it succeeds, {@code false} otherwise
+ * Helper method to mark a network enabled for network selection.
*/
- boolean enableNetwork(WifiConfiguration config, boolean disableOthers, int uid) {
+ private void setNetworkSelectionEnabled(WifiConfiguration config) {
+ NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+ 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();
+ if (mListener != null) mListener.onSavedNetworkEnabled(config.networkId);
+ }
+
+ /**
+ * Helper method to mark a network temporarily disabled for network selection.
+ */
+ private void setNetworkSelectionTemporarilyDisabled(
+ WifiConfiguration config, int disableReason) {
+ NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+ 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);
+ if (mListener != null) mListener.onSavedNetworkTemporarilyDisabled(config.networkId);
+ }
+
+ /**
+ * Helper method to mark a network permanently disabled for network selection.
+ */
+ private void setNetworkSelectionPermanentlyDisabled(
+ WifiConfiguration config, int disableReason) {
+ NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+ status.setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
+ status.setDisableTime(
+ NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
+ status.setNetworkSelectionDisableReason(disableReason);
+ if (mListener != null) mListener.onSavedNetworkPermanentlyDisabled(config.networkId);
+ }
+
+ /**
+ * 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(config);
+ setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
+ } else if (reason < NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
+ setNetworkSelectionTemporarilyDisabled(config, reason);
+ } else {
+ setNetworkSelectionPermanentlyDisabled(config, reason);
+ setNetworkStatus(config, WifiConfiguration.Status.DISABLED);
+ }
+ localLog("setNetworkSelectionStatus: configKey=" + config.configKey()
+ + " networkStatus=" + networkStatus.getNetworkStatusString() + " disableReason="
+ + networkStatus.getNetworkDisableReasonString() + " at="
+ + createDebugTimeStampString(mClock.getWallClockMillis()));
+ saveToStore(false);
+ 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 Network Selector.
+ * 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) {
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);
- }
- updateLastConnectUid(config, uid);
- writeKnownNetworkHistory();
- sendConfiguredNetworksChangedBroadcast();
- } else {
- if (sVDBG) 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());
- }
-
- /**
- * Disable a network in wpa_supplicant.
- */
- boolean disableNetworkNative(WifiConfiguration config) {
- return mWifiConfigStore.disableNetwork(config);
- }
-
- /**
- * Disable all networks in wpa_supplicant.
- */
- void disableAllNetworksNative() {
- mWifiConfigStore.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));
- }
-
- /**
- * Update a network according to the update reason and its current state
- * @param netId The network ID of the network need update
- * @param reason The reason to update the network
- * @return false if no change made to the input configure file, can due to error or need not
- * true the input config file has been changed
- */
- boolean updateNetworkSelectionStatus(int netId, int reason) {
- WifiConfiguration config = getWifiConfiguration(netId);
return updateNetworkSelectionStatus(config, reason);
}
/**
- * Update a network according to the update reason and its current state
- * @param config the network need update
- * @param reason The reason to update the network
- * @return false if no change made to the input configure file, can due to error or need not
- * true the input config file has been changed
+ * Update whether a network is currently not recommended by {@link RecommendedNetworkEvaluator}.
+ *
+ * @param networkId network ID of the network to be updated
+ * @param notRecommended whether this network is not recommended
+ * @return true if the network is updated, false otherwise
*/
- boolean updateNetworkSelectionStatus(WifiConfiguration config, int reason) {
+ public boolean updateNetworkNotRecommended(int networkId, boolean notRecommended) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
if (config == null) {
return false;
}
- WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
- if (reason == WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE) {
- updateNetworkStatus(config, WifiConfiguration.NetworkSelectionStatus
- .NETWORK_SELECTION_ENABLE);
- localLog("Enable network:" + config.configKey());
- return true;
+ config.getNetworkSelectionStatus().setNotRecommended(notRecommended);
+ if (mVerboseLoggingEnabled) {
+ localLog("updateNetworkRecommendation: configKey=" + config.configKey()
+ + " notRecommended=" + notRecommended);
}
-
- networkStatus.incrementDisableReasonCounter(reason);
- if (DBG) {
- localLog("Network:" + config.SSID + "disable counter of "
- + WifiConfiguration.NetworkSelectionStatus.getNetworkDisableReasonString(reason)
- + " is: " + networkStatus.getDisableReasonCounter(reason) + "and threshold is: "
- + NETWORK_SELECTION_DISABLE_THRESHOLD[reason]);
- }
-
- if (networkStatus.getDisableReasonCounter(reason)
- >= NETWORK_SELECTION_DISABLE_THRESHOLD[reason]) {
- return updateNetworkStatus(config, reason);
- }
+ saveToStore(false);
return true;
}
/**
- * 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.
- * @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
+ * 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.
*/
- public boolean tryEnableQualifiedNetwork(int networkId) {
- WifiConfiguration config = getWifiConfiguration(networkId);
- if (config == null) {
- localLog("updateQualifiedNetworkstatus invalid network.");
- return false;
- }
- return tryEnableQualifiedNetwork(config);
- }
-
- /**
- * 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
- */
- private boolean tryEnableQualifiedNetwork(WifiConfiguration config) {
- WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
+ private boolean tryEnableNetwork(WifiConfiguration config) {
+ NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
if (networkStatus.isNetworkTemporaryDisabled()) {
- //time difference in minutes
- long timeDifference =
- (mClock.elapsedRealtime() - networkStatus.getDisableTime()) / 1000 / 60;
- if (timeDifference < 0 || timeDifference
- >= NETWORK_SELECTION_DISABLE_TIMEOUT[
- networkStatus.getNetworkSelectionDisableReason()]) {
- updateNetworkSelectionStatus(config.networkId,
- networkStatus.NETWORK_SELECTION_ENABLE);
- return true;
+ 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;
}
/**
- * Update a network's status. Note that there is no saveConfig operation.
- * @param config network to be updated
- * @param reason reason code for updated
- * @return false if no change made to the input configure file, can due to error or need not
- * true the input config file has been changed
+ * 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.
*/
- boolean updateNetworkStatus(WifiConfiguration config, int reason) {
- localLog("updateNetworkStatus:" + (config == null ? null : config.SSID));
+ public boolean tryEnableNetwork(int networkId) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
if (config == null) {
return false;
}
+ return tryEnableNetwork(config);
+ }
- WifiConfiguration.NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus();
- if (reason < 0 || reason >= WifiConfiguration.NetworkSelectionStatus
- .NETWORK_SELECTION_DISABLED_MAX) {
- localLog("Invalid Network disable reason:" + reason);
+ /**
+ * Enable a network using the public {@link WifiManager#enableNetwork(int, boolean)} API.
+ *
+ * @param networkId network ID of the network that needs the update.
+ * @param disableOthers Whether to disable all other networks or not. This is used to indicate
+ * that the app requested connection to a specific network.
+ * @param uid uid of the app requesting the update.
+ * @return true if it succeeds, false otherwise
+ */
+ public boolean enableNetwork(int networkId, boolean disableOthers, int uid) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Enabling network " + networkId + " (disableOthers " + disableOthers + ")");
+ }
+ if (!doesUidBelongToCurrentUser(uid)) {
+ Log.e(TAG, "UID " + uid + " not visible to the current user");
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");
- }
- return false;
- }
- networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
- .NETWORK_SELECTION_ENABLED);
- networkStatus.setNetworkSelectionDisableReason(reason);
- networkStatus.setDisableTime(
- 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);
- }
- 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! "
- + WifiConfiguration.NetworkSelectionStatus
- .getNetworkDisableReasonString(reason));
- }
- return false;
- } else if (networkStatus.isNetworkTemporaryDisabled()
- && reason < WifiConfiguration.NetworkSelectionStatus
- .DISABLED_TLS_VERSION_MISMATCH) {
- //alreay temporarily disable
- if (DBG) {
- localLog("Do nothing. Already temporarily disabled! "
- + 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));
- }
- if (reason < WifiConfiguration.NetworkSelectionStatus.DISABLED_TLS_VERSION_MISMATCH) {
- networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
- .NETWORK_SELECTION_TEMPORARY_DISABLED);
- networkStatus.setDisableTime(mClock.elapsedRealtime());
- } else {
- networkStatus.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
- .NETWORK_SELECTION_PERMANENTLY_DISABLED);
- }
- 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);
- }
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ 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;
+ }
+ if (!updateNetworkSelectionStatus(
+ networkId, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE)) {
+ return false;
+ }
+ if (disableOthers) {
+ setLastSelectedNetwork(networkId);
+ }
+ saveToStore(true);
return true;
}
/**
- * Save the configured networks in supplicant to disk
- * @return {@code true} if it succeeds, {@code false} otherwise
+ * 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 true if it succeeds, false otherwise
*/
- boolean saveConfig() {
- return mWifiConfigStore.saveConfig();
+ public boolean disableNetwork(int networkId, int uid) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Disabling network " + networkId);
+ }
+ if (!doesUidBelongToCurrentUser(uid)) {
+ Log.e(TAG, "UID " + uid + " not visible to the current user");
+ return false;
+ }
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ 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;
+ }
+ if (!updateNetworkSelectionStatus(
+ networkId, NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) {
+ return false;
+ }
+ if (networkId == mLastSelectedNetworkId) {
+ clearLastSelectedNetwork();
+ }
+ saveToStore(true);
+ return true;
}
/**
- * Start WPS pin method configuration with pin obtained
- * from the access point
- * @param config WPS configuration
- * @return Wps result containing status and pin
+ * Checks if the |uid| has the necessary permission to force a connection to a network
+ * 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 true if |uid| has the necessary permission to trigger explicit connection to the
+ * network, false otherwise.
+ * Note: This returns true only for the system settings/sysui app which holds the
+ * {@link android.Manifest.permission#OVERRIDE_WIFI_CONFIG} permission. We don't want to let
+ * any other app force connection to a network.
*/
- WpsResult startWpsWithPinFromAccessPoint(WpsInfo config) {
- return mWifiConfigStore.startWpsWithPinFromAccessPoint(
- config, mConfiguredNetworks.valuesForCurrentUser());
+ public boolean checkAndUpdateLastConnectUid(int networkId, int uid) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Update network last connect UID for " + networkId);
+ }
+ if (!doesUidBelongToCurrentUser(uid)) {
+ Log.e(TAG, "UID " + uid + " not visible to the current user");
+ return false;
+ }
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ 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;
}
/**
- * Start WPS pin method configuration with obtained
- * from the device
- * @return WpsResult indicating status and pin
+ * Updates a network configuration after a successful connection to it.
+ *
+ * This method updates the following WifiConfiguration elements:
+ * 1. Set the |lastConnected| timestamp.
+ * 2. Increment |numAssociation| counter.
+ * 3. Clear the disable reason counters in the associated |NetworkSelectionStatus|.
+ * 4. Set the hasEverConnected| flag in the associated |NetworkSelectionStatus|.
+ * 5. Sets the status of network as |CURRENT|.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @return true if the network was found, false otherwise.
*/
- WpsResult startWpsWithPinFromDevice(WpsInfo config) {
- return mWifiConfigStore.startWpsWithPinFromDevice(
- config, mConfiguredNetworks.valuesForCurrentUser());
+ public boolean updateNetworkAfterConnect(int networkId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Update network after connect for " + networkId);
+ }
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
+ }
+ config.lastConnected = mClock.getWallClockMillis();
+ config.numAssociation++;
+ config.getNetworkSelectionStatus().clearDisableReasonCounter();
+ config.getNetworkSelectionStatus().setHasEverConnected(true);
+ setNetworkStatus(config, WifiConfiguration.Status.CURRENT);
+ saveToStore(false);
+ return true;
}
/**
- * Start WPS push button configuration
- * @param config WPS configuration
- * @return WpsResult indicating status and pin
+ * Updates a network configuration after disconnection from it.
+ *
+ * This method updates the following WifiConfiguration elements:
+ * 1. Set the |lastDisConnected| timestamp.
+ * 2. Sets the status of network back to |ENABLED|.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @return true if the network was found, false otherwise.
*/
- WpsResult startWpsPbc(WpsInfo config) {
- return mWifiConfigStore.startWpsPbc(
- config, mConfiguredNetworks.valuesForCurrentUser());
+ public boolean updateNetworkAfterDisconnect(int networkId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Update network after disconnect for " + networkId);
+ }
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
+ }
+ config.lastDisconnected = mClock.getWallClockMillis();
+ // If the network hasn't been disabled, mark it back as
+ // enabled after disconnection.
+ if (config.status == WifiConfiguration.Status.CURRENT) {
+ setNetworkStatus(config, WifiConfiguration.Status.ENABLED);
+ }
+ saveToStore(false);
+ return true;
}
/**
- * Fetch the static IP configuration for a given network id
+ * Set default GW MAC address for the provided network.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @param macAddress MAC address of the gateway to be set.
+ * @return true if the network was found, false otherwise.
*/
- StaticIpConfiguration getStaticIpConfiguration(int netId) {
- WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
- if (config != null) {
- return config.getStaticIpConfiguration();
+ public boolean setNetworkDefaultGwMacAddress(int networkId, String macAddress) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
}
- return null;
+ config.defaultGwMacAddress = macAddress;
+ return true;
}
/**
- * Set the static IP configuration for a given network id
+ * Clear the {@link NetworkSelectionStatus#mCandidate},
+ * {@link NetworkSelectionStatus#mCandidateScore} &
+ * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
+ *
+ * This is invoked by Network Selector at the start of every selection procedure to clear all
+ * configured networks' scan-result-candidates.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @return true if the network was found, false otherwise.
*/
- void setStaticIpConfiguration(int netId, StaticIpConfiguration staticIpConfiguration) {
- WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
- if (config != null) {
- config.setStaticIpConfiguration(staticIpConfiguration);
+ public boolean clearNetworkCandidateScanResult(int networkId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Clear network candidate scan result for " + networkId);
}
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
+ }
+ config.getNetworkSelectionStatus().setCandidate(null);
+ config.getNetworkSelectionStatus().setCandidateScore(Integer.MIN_VALUE);
+ config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(false);
+ return true;
}
/**
- * set default GW MAC address
+ * Set the {@link NetworkSelectionStatus#mCandidate},
+ * {@link NetworkSelectionStatus#mCandidateScore} &
+ * {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} for the provided network.
+ *
+ * This is invoked by Network Selector when it sees a network during network selection procedure
+ * to set the scan result candidate.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @param scanResult Candidate ScanResult associated with this network.
+ * @param score Score assigned to the candidate.
+ * @return true if the network was found, false otherwise.
*/
- void setDefaultGwMacAddress(int netId, String macAddress) {
- WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
- if (config != null) {
- //update defaultGwMacAddress
- config.defaultGwMacAddress = macAddress;
+ public boolean setNetworkCandidateScanResult(int networkId, ScanResult scanResult, int score) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Set network candidate scan result " + scanResult + " for " + networkId);
}
- }
-
-
- /**
- * Fetch the proxy properties for a given network id
- * @param netId id
- * @return ProxyInfo for the network id
- */
- ProxyInfo getProxyProperties(int netId) {
- WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
- if (config != null) {
- return config.getHttpProxy();
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
}
- return null;
+ config.getNetworkSelectionStatus().setCandidate(scanResult);
+ config.getNetworkSelectionStatus().setCandidateScore(score);
+ config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true);
+ return true;
}
/**
- * Return if the specified network is using static IP
- * @param netId id
- * @return {@code true} if using static ip for netId
+ * Iterate through all the saved networks and remove the provided configuration from the
+ * {@link NetworkSelectionStatus#mConnectChoice} from them.
+ *
+ * This is invoked when a network is removed from our records.
+ *
+ * @param connectChoiceConfigKey ConfigKey corresponding to the network that is being removed.
*/
- boolean isUsingStaticIp(int netId) {
- WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
- if (config != null && config.getIpAssignment() == IpAssignment.STATIC) {
- return true;
+ private void removeConnectChoiceFromAllNetworks(String connectChoiceConfigKey) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Removing connect choice from all networks " + connectChoiceConfigKey);
}
- return false;
- }
-
- boolean isEphemeral(int netId) {
- WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
- return config != null && config.ephemeral;
- }
-
- boolean getMeteredHint(int netId) {
- WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(netId);
- return config != null && config.meteredHint;
- }
-
- /**
- * Should be called when a single network configuration is made.
- * @param network The network configuration that 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 sendConfiguredNetworksChangedBroadcast(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);
- intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network);
- intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- /**
- * Should be called when multiple network configuration changes are made.
- */
- 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);
- }
-
- void loadConfiguredNetworks() {
-
- final Map<String, WifiConfiguration> configs = new HashMap<>();
- final SparseArray<Map<String, String>> networkExtras = new SparseArray<>();
- mLastPriority = mWifiConfigStore.loadNetworks(configs, networkExtras);
-
- readNetworkHistory(configs);
- readPasspointConfig(configs, networkExtras);
-
- // We are only now updating mConfiguredNetworks for two reasons:
- // 1) The information required to compute configKeys is spread across wpa_supplicant.conf
- // and networkHistory.txt. Thus, we had to load both files first.
- // 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) {
- log("Ignoring network " + config.networkId + " because the configKey loaded "
- + "from wpa_supplicant.conf is not valid.");
- }
- mWifiConfigStore.removeNetwork(config);
- continue;
- }
- mConfiguredNetworks.put(config);
- }
-
- readIpAndProxyConfigurations();
-
- sendConfiguredNetworksChangedBroadcast();
-
- if (mShowNetworks) {
- localLog("loadConfiguredNetworks loaded " + mConfiguredNetworks.sizeForAllUsers()
- + " networks (for all users)");
- }
-
- 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(WifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE);
- }
- }
-
- private void logContents(String file) {
- localLogAndLogcat("--- Begin " + file + " ---");
- BufferedReader reader = null;
- try {
- reader = new BufferedReader(new FileReader(file));
- for (String line = reader.readLine(); line != null; line = reader.readLine()) {
- localLogAndLogcat(line);
- }
- } catch (FileNotFoundException e) {
- localLog("Could not open " + file + ", " + e);
- Log.w(TAG, "Could not open " + file + ", " + e);
- } catch (IOException e) {
- localLog("Could not read " + file + ", " + e);
- Log.w(TAG, "Could not read " + file + ", " + e);
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (IOException e) {
- // Just ignore the fact that we couldn't close
- }
- }
- localLogAndLogcat("--- End " + file + " Contents ---");
- }
-
- private Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
- return mWifiConfigStore.readNetworkVariablesFromSupplicantFile(key);
- }
-
- private String readNetworkVariableFromSupplicantFile(String configKey, String key) {
- long start = SystemClock.elapsedRealtimeNanos();
- Map<String, String> data = mWifiConfigStore.readNetworkVariablesFromSupplicantFile(key);
- long end = SystemClock.elapsedRealtimeNanos();
-
- if (sVDBG) {
- localLog("readNetworkVariableFromSupplicantFile configKey=[" + configKey + "] key="
- + key + " duration=" + (long) (end - start));
- }
- return data.get(configKey);
- }
-
- boolean needsUnlockedKeyStore() {
-
- // Any network using certificates to authenticate access requires
- // unlocked key store; unless the certificates can be stored with
- // hardware encryption
-
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
-
- if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP)
- && config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
-
- if (needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- void readPasspointConfig(Map<String, WifiConfiguration> configs,
- SparseArray<Map<String, String>> networkExtras) {
- List<HomeSP> homeSPs;
- try {
- homeSPs = mMOManager.loadAllSPs();
- } catch (IOException e) {
- loge("Could not read " + PPS_FILE + " : " + e);
+ if (connectChoiceConfigKey == null) {
return;
}
-
- int matchedConfigs = 0;
- for (HomeSP homeSp : homeSPs) {
- String fqdn = homeSp.getFQDN();
- Log.d(TAG, "Looking for " + fqdn);
- for (WifiConfiguration config : configs.values()) {
- Log.d(TAG, "Testing " + config.SSID);
-
- if (config.enterpriseConfig == null) {
- continue;
- }
- final String configFqdn =
- networkExtras.get(config.networkId).get(WifiConfigStore.ID_STRING_KEY_FQDN);
- if (configFqdn != null && configFqdn.equals(fqdn)) {
- Log.d(TAG, "Matched " + configFqdn + " with " + config.networkId);
- ++matchedConfigs;
- config.FQDN = fqdn;
- config.providerFriendlyName = homeSp.getFriendlyName();
-
- HashSet<Long> roamingConsortiumIds = homeSp.getRoamingConsortiums();
- config.roamingConsortiumIds = new long[roamingConsortiumIds.size()];
- int i = 0;
- for (long id : roamingConsortiumIds) {
- config.roamingConsortiumIds[i] = id;
- i++;
- }
- IMSIParameter imsiParameter = homeSp.getCredential().getImsi();
- config.enterpriseConfig.setPlmn(
- imsiParameter != null ? imsiParameter.toString() : null);
- config.enterpriseConfig.setRealm(homeSp.getCredential().getRealm());
- }
+ for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
+ WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
+ String connectChoice = status.getConnectChoice();
+ if (TextUtils.equals(connectChoice, connectChoiceConfigKey)) {
+ Log.d(TAG, "remove connect choice:" + connectChoice + " from " + config.SSID
+ + " : " + config.networkId);
+ clearNetworkConnectChoice(config.networkId);
}
}
-
- Log.d(TAG, "loaded " + matchedConfigs + " passpoint configs");
- }
-
- public void writePasspointConfigs(final String fqdn, final HomeSP homeSP) {
- mWriter.write(PPS_FILE, new DelayedDiskWrite.Writer() {
- @Override
- public void onWriteCalled(DataOutputStream out) throws IOException {
- try {
- if (homeSP != null) {
- mMOManager.addSP(homeSP);
- } else {
- mMOManager.removeSP(fqdn);
- }
- } catch (IOException e) {
- loge("Could not write " + PPS_FILE + " : " + e);
- }
- }
- }, false);
}
/**
- * Write network history, WifiConfigurations and mScanDetailCaches to file.
+ * Clear the {@link NetworkSelectionStatus#mConnectChoice} &
+ * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @return true if the network was found, false otherwise.
*/
- private void readNetworkHistory(Map<String, WifiConfiguration> configs) {
- mWifiNetworkHistory.readNetworkHistory(configs,
- mScanDetailCaches,
- mDeletedEphemeralSSIDs);
+ public boolean clearNetworkConnectChoice(int networkId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Clear network connect choice for " + networkId);
+ }
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
+ }
+ config.getNetworkSelectionStatus().setConnectChoice(null);
+ config.getNetworkSelectionStatus().setConnectChoiceTimestamp(
+ NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
+ saveToStore(false);
+ return true;
}
/**
- * Read Network history from file, merge it into mConfiguredNetowrks and mScanDetailCaches
+ * Set the {@link NetworkSelectionStatus#mConnectChoice} &
+ * {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
+ *
+ * This is invoked by Network Selector when the user overrides the currently connected network
+ * choice.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over
+ * this network.
+ * @param timestamp timestamp at which the choice was made.
+ * @return true if the network was found, false otherwise.
*/
- public void writeKnownNetworkHistory() {
- final List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>();
- for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
- networks.add(new WifiConfiguration(config));
+ public boolean setNetworkConnectChoice(
+ int networkId, String connectChoiceConfigKey, long timestamp) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId);
}
- mWifiNetworkHistory.writeKnownNetworkHistory(networks,
- mScanDetailCaches,
- mDeletedEphemeralSSIDs);
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
+ }
+ config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey);
+ config.getNetworkSelectionStatus().setConnectChoiceTimestamp(timestamp);
+ saveToStore(false);
+ return true;
}
- public void setAndEnableLastSelectedConfiguration(int netId) {
- if (sVDBG) {
- logd("setLastSelectedConfiguration " + Integer.toString(netId));
+ /**
+ * Increments the number of no internet access reports in the provided network.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @return true if the network was found, false otherwise.
+ */
+ public boolean incrementNetworkNoInternetAccessReports(int networkId) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
}
- if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
- mLastSelectedConfiguration = null;
- mLastSelectedTimeStamp = -1;
- } else {
- WifiConfiguration selected = getWifiConfiguration(netId);
- if (selected == null) {
- mLastSelectedConfiguration = null;
- mLastSelectedTimeStamp = -1;
- } else {
- mLastSelectedConfiguration = selected.configKey();
- mLastSelectedTimeStamp = mClock.elapsedRealtime();
- updateNetworkSelectionStatus(netId,
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
- if (sVDBG) {
- logd("setLastSelectedConfiguration now: " + mLastSelectedConfiguration);
- }
- }
- }
+ config.numNoInternetAccessReports++;
+ return true;
}
- public void setLatestUserSelectedConfiguration(WifiConfiguration network) {
- if (network != null) {
- mLastSelectedConfiguration = network.configKey();
- mLastSelectedTimeStamp = mClock.elapsedRealtime();
+ /**
+ * Sets the internet access is validated or not in the provided network.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @param validated Whether access is validated or not.
+ * @return true if the network was found, false otherwise.
+ */
+ public boolean setNetworkValidatedInternetAccess(int networkId, boolean validated) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
}
+ config.validatedInternetAccess = validated;
+ config.numNoInternetAccessReports = 0;
+ saveToStore(false);
+ return true;
}
- public String getLastSelectedConfiguration() {
- return mLastSelectedConfiguration;
+ /**
+ * Sets whether the internet access is expected or not in the provided network.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @param expected Whether access is expected or not.
+ * @return true if the network was found, false otherwise.
+ */
+ public boolean setNetworkNoInternetAccessExpected(int networkId, boolean expected) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
+ }
+ config.noInternetAccessExpected = expected;
+ return true;
}
+ /**
+ * Helper method to clear out the {@link #mNextNetworkId} user/app network selection. This
+ * is done when either the corresponding network is either removed or disabled.
+ */
+ private void clearLastSelectedNetwork() {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Clearing last selected network");
+ }
+ mLastSelectedNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
+ mLastSelectedTimeStamp = NetworkSelectionStatus.INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP;
+ }
+
+ /**
+ * Helper method to mark a network as the last selected one by an app/user. This is set
+ * when an app invokes {@link #enableNetwork(int, boolean, int)} with |disableOthers| flag set.
+ * This is used by network selector to assign a special bonus during network selection.
+ */
+ private void setLastSelectedNetwork(int networkId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Setting last selected network to " + networkId);
+ }
+ mLastSelectedNetworkId = networkId;
+ mLastSelectedTimeStamp = mClock.getElapsedSinceBootMillis();
+ }
+
+ /**
+ * Retrieve the network Id corresponding to the last network that was explicitly selected by
+ * an app/user.
+ *
+ * @return network Id corresponding to the last selected network.
+ */
+ public int getLastSelectedNetwork() {
+ return mLastSelectedNetworkId;
+ }
+
+ /**
+ * Retrieve the configKey corresponding to the last network that was explicitly selected by
+ * an app/user.
+ *
+ * @return network Id corresponding to the last selected network.
+ */
+ public String getLastSelectedNetworkConfigKey() {
+ if (mLastSelectedNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
+ return "";
+ }
+ WifiConfiguration config = getInternalConfiguredNetwork(mLastSelectedNetworkId);
+ if (config == null) {
+ return "";
+ }
+ return config.configKey();
+ }
+
+ /**
+ * Retrieve the time stamp at which a network was explicitly selected by an app/user.
+ *
+ * @return timestamp in milliseconds from boot when this was set.
+ */
public long getLastSelectedTimeStamp() {
return mLastSelectedTimeStamp;
}
- public boolean isLastSelectedConfiguration(WifiConfiguration config) {
- return (mLastSelectedConfiguration != null
- && config != null
- && mLastSelectedConfiguration.equals(config.configKey()));
+ /**
+ * 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.
+ */
+ public ScanDetailCache getScanDetailCacheForNetwork(int networkId) {
+ return mScanDetailCaches.get(networkId);
}
- private void writeIpAndProxyConfigurations() {
- final SparseArray<IpConfiguration> networks = new SparseArray<IpConfiguration>();
- for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
- if (!config.ephemeral) {
- networks.put(configKey(config), config.getIpConfiguration());
- }
- }
-
- mIpconfigStore.writeIpAndProxyConfigurations(IP_CONFIG_FILE, networks);
- }
-
- private void readIpAndProxyConfigurations() {
- SparseArray<IpConfiguration> networks =
- mIpconfigStore.readIpAndProxyConfigurations(IP_CONFIG_FILE);
-
- if (networks == null || networks.size() == 0) {
- // IpConfigStore.readIpAndProxyConfigurations has already logged an error.
- return;
- }
-
- for (int i = 0; i < networks.size(); i++) {
- int id = networks.keyAt(i);
- WifiConfiguration config = mConfiguredNetworks.getByConfigKeyIDForAllUsers(id);
- // This is the only place the map is looked up through a (dangerous) hash-value!
-
- if (config == null || config.ephemeral) {
- logd("configuration found for missing network, nid=" + id
- + ", ignored, networks.size=" + Integer.toString(networks.size()));
- } else {
- config.setIpConfiguration(networks.valueAt(i));
- }
- }
- }
-
- private NetworkUpdateResult addOrUpdateNetworkNative(WifiConfiguration config, int uid) {
- /*
- * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
- * network configuration. Otherwise, the networkId should
- * refer to an existing configuration.
- */
-
- if (sVDBG) localLog("addOrUpdateNetworkNative " + config.getPrintableSsid());
- if (config.isPasspoint() && !mMOManager.isEnabled()) {
- Log.e(TAG, "Passpoint is not enabled");
- return new NetworkUpdateResult(INVALID_NETWORK_ID);
- }
-
- boolean newNetwork = false;
- boolean existingMO = false;
- WifiConfiguration currentConfig;
- // networkId of INVALID_NETWORK_ID means we want to create a new network
- if (config.networkId == INVALID_NETWORK_ID) {
- // Try to fetch the existing config using configKey
- currentConfig = mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
- if (currentConfig != null) {
- config.networkId = currentConfig.networkId;
- } else {
- if (mMOManager.getHomeSP(config.FQDN) != null) {
- logd("addOrUpdateNetworkNative passpoint " + config.FQDN
- + " was found, but no network Id");
- existingMO = true;
- }
- newNetwork = true;
- }
- } else {
- // Fetch the existing config using networkID
- currentConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
- }
-
- // originalConfig is used to check for credential and config changes that would cause
- // HasEverConnected to be set to false.
- WifiConfiguration originalConfig = new WifiConfiguration(currentConfig);
-
- if (!mWifiConfigStore.addOrUpdateNetwork(config, currentConfig,
- mSystemSupportsFastBssTransition)) {
- return new NetworkUpdateResult(INVALID_NETWORK_ID);
- }
- int netId = config.networkId;
- String savedConfigKey = config.configKey();
-
- /* An update of the network variables requires reading them
- * back from the supplicant to update mConfiguredNetworks.
- * This is because some of the variables (SSID, wep keys &
- * passphrases) reflect different values when read back than
- * when written. For example, wep key is stored as * irrespective
- * of the value sent to the supplicant.
- */
- if (currentConfig == null) {
- currentConfig = new WifiConfiguration();
- currentConfig.setIpAssignment(IpAssignment.DHCP);
- currentConfig.setProxySettings(ProxySettings.NONE);
- currentConfig.networkId = netId;
- if (config != null) {
- // Carry over the creation parameters
- currentConfig.selfAdded = config.selfAdded;
- currentConfig.didSelfAdd = config.didSelfAdd;
- currentConfig.ephemeral = config.ephemeral;
- currentConfig.meteredHint = config.meteredHint;
- currentConfig.useExternalScores = config.useExternalScores;
- currentConfig.lastConnectUid = config.lastConnectUid;
- currentConfig.lastUpdateUid = config.lastUpdateUid;
- currentConfig.creatorUid = config.creatorUid;
- currentConfig.creatorName = config.creatorName;
- currentConfig.lastUpdateName = config.lastUpdateName;
- currentConfig.peerWifiConfiguration = config.peerWifiConfiguration;
- currentConfig.FQDN = config.FQDN;
- currentConfig.providerFriendlyName = config.providerFriendlyName;
- currentConfig.roamingConsortiumIds = config.roamingConsortiumIds;
- currentConfig.validatedInternetAccess = config.validatedInternetAccess;
- currentConfig.numNoInternetAccessReports = config.numNoInternetAccessReports;
- currentConfig.updateTime = config.updateTime;
- currentConfig.creationTime = config.creationTime;
- currentConfig.shared = config.shared;
- currentConfig.isCarrierNetwork = config.isCarrierNetwork;
- }
- if (DBG) {
- log("created new config netId=" + Integer.toString(netId)
- + " uid=" + Integer.toString(currentConfig.creatorUid)
- + " name=" + currentConfig.creatorName);
- }
- }
-
- /* save HomeSP object for passpoint networks */
- HomeSP homeSP = null;
-
- if (!existingMO && config.isPasspoint()) {
- try {
- if (config.updateIdentifier == null) { // Only create an MO for r1 networks
- Credential credential =
- new Credential(config.enterpriseConfig, mKeyStore, !newNetwork);
- HashSet<Long> roamingConsortiumIds = new HashSet<Long>();
- for (Long roamingConsortiumId : config.roamingConsortiumIds) {
- roamingConsortiumIds.add(roamingConsortiumId);
- }
-
- homeSP = new HomeSP(Collections.<String, Long>emptyMap(), config.FQDN,
- roamingConsortiumIds, Collections.<String>emptySet(),
- Collections.<Long>emptySet(), Collections.<Long>emptyList(),
- config.providerFriendlyName, null, credential);
-
- log("created a homeSP object for " + config.networkId + ":" + config.SSID);
- }
-
- /* fix enterprise config properties for passpoint */
- currentConfig.enterpriseConfig.setRealm(config.enterpriseConfig.getRealm());
- currentConfig.enterpriseConfig.setPlmn(config.enterpriseConfig.getPlmn());
- } catch (IOException ioe) {
- Log.e(TAG, "Failed to create Passpoint config: " + ioe);
- return new NetworkUpdateResult(INVALID_NETWORK_ID);
- }
- }
-
- if (uid != WifiConfiguration.UNKNOWN_UID) {
- if (newNetwork) {
- currentConfig.creatorUid = uid;
- } else {
- currentConfig.lastUpdateUid = uid;
- }
- }
-
- // For debug, record the time the configuration was modified
- StringBuilder sb = new StringBuilder();
- sb.append("time=");
- Calendar c = Calendar.getInstance();
- c.setTimeInMillis(mClock.currentTimeMillis());
- sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
-
- if (newNetwork) {
- 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.
- currentConfig.ephemeral = false;
- if (DBG) {
- log("remove ephemeral status netId=" + Integer.toString(netId)
- + " " + currentConfig.configKey());
- }
- }
-
- if (sVDBG) 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)) {
- loge("Failed to set network metadata. Removing config " + config.networkId);
- mWifiConfigStore.removeNetwork(config);
- return new NetworkUpdateResult(INVALID_NETWORK_ID);
- }
- }
-
- boolean passwordChanged = false;
- // check passed in config to see if it has more than a password set.
- if (!newNetwork && config.preSharedKey != null && !config.preSharedKey.equals("*")) {
- passwordChanged = true;
- }
-
- if (newNetwork || passwordChanged || wasCredentialChange(originalConfig, currentConfig)) {
- currentConfig.getNetworkSelectionStatus().setHasEverConnected(false);
- }
-
- // Persist configuration paramaters that are not saved by supplicant.
- if (config.lastUpdateName != null) {
- currentConfig.lastUpdateName = config.lastUpdateName;
- }
- if (config.lastUpdateUid != WifiConfiguration.UNKNOWN_UID) {
- currentConfig.lastUpdateUid = config.lastUpdateUid;
- }
-
- mConfiguredNetworks.put(currentConfig);
-
- NetworkUpdateResult result =
- writeIpAndProxyConfigurationsOnChange(currentConfig, config, newNetwork);
- result.setIsNewNetwork(newNetwork);
- result.setNetworkId(netId);
-
- if (homeSP != null) {
- writePasspointConfigs(null, homeSP);
- }
-
- saveConfig();
- writeKnownNetworkHistory();
-
- return result;
- }
-
- private boolean wasBitSetUpdated(BitSet originalBitSet, BitSet currentBitSet) {
- if (originalBitSet != null && currentBitSet != null) {
- // both configs have values set, check if they are different
- if (!originalBitSet.equals(currentBitSet)) {
- // the BitSets are different
- return true;
- }
- } else if (originalBitSet != null || currentBitSet != null) {
- return true;
- }
- return false;
- }
-
- private boolean wasCredentialChange(WifiConfiguration originalConfig,
- WifiConfiguration currentConfig) {
- // Check if any core WifiConfiguration parameters changed that would impact new connections
- if (originalConfig == null) {
- return true;
- }
-
- if (wasBitSetUpdated(originalConfig.allowedKeyManagement,
- currentConfig.allowedKeyManagement)) {
- return true;
- }
-
- if (wasBitSetUpdated(originalConfig.allowedProtocols, currentConfig.allowedProtocols)) {
- return true;
- }
-
- if (wasBitSetUpdated(originalConfig.allowedAuthAlgorithms,
- currentConfig.allowedAuthAlgorithms)) {
- return true;
- }
-
- if (wasBitSetUpdated(originalConfig.allowedPairwiseCiphers,
- currentConfig.allowedPairwiseCiphers)) {
- return true;
- }
-
- if (wasBitSetUpdated(originalConfig.allowedGroupCiphers,
- currentConfig.allowedGroupCiphers)) {
- return true;
- }
-
- if (originalConfig.wepKeys != null && currentConfig.wepKeys != null) {
- if (originalConfig.wepKeys.length == currentConfig.wepKeys.length) {
- for (int i = 0; i < originalConfig.wepKeys.length; i++) {
- if (!Objects.equals(originalConfig.wepKeys[i], currentConfig.wepKeys[i])) {
- return true;
- }
- }
- } else {
- return true;
- }
- }
-
- if (originalConfig.hiddenSSID != currentConfig.hiddenSSID) {
- return true;
- }
-
- if (originalConfig.requirePMF != currentConfig.requirePMF) {
- return true;
- }
-
- if (wasEnterpriseConfigChange(originalConfig.enterpriseConfig,
- currentConfig.enterpriseConfig)) {
- return true;
- }
- return false;
- }
-
-
- protected boolean wasEnterpriseConfigChange(WifiEnterpriseConfig originalEnterpriseConfig,
- WifiEnterpriseConfig currentEnterpriseConfig) {
- if (originalEnterpriseConfig != null && currentEnterpriseConfig != null) {
- if (originalEnterpriseConfig.getEapMethod() != currentEnterpriseConfig.getEapMethod()) {
- return true;
- }
-
- if (originalEnterpriseConfig.getPhase2Method()
- != currentEnterpriseConfig.getPhase2Method()) {
- return true;
- }
-
- X509Certificate[] originalCaCerts = originalEnterpriseConfig.getCaCertificates();
- X509Certificate[] currentCaCerts = currentEnterpriseConfig.getCaCertificates();
-
- if (originalCaCerts != null && currentCaCerts != null) {
- if (originalCaCerts.length == currentCaCerts.length) {
- for (int i = 0; i < originalCaCerts.length; i++) {
- if (!originalCaCerts[i].equals(currentCaCerts[i])) {
- return true;
- }
- }
- } else {
- // number of aliases is different, so the configs are different
- return true;
- }
- } else {
- // one of the enterprise configs may have aliases
- if (originalCaCerts != null || currentCaCerts != null) {
- return true;
- }
- }
- } else {
- // One of the configs may have an enterpriseConfig
- if (originalEnterpriseConfig != null || currentEnterpriseConfig != null) {
- return true;
- }
- }
- return false;
- }
-
- public WifiConfiguration getWifiConfigForHomeSP(HomeSP homeSP) {
- WifiConfiguration config = mConfiguredNetworks.getByFQDNForCurrentUser(homeSP.getFQDN());
- if (config == null) {
- Log.e(TAG, "Could not find network for homeSP " + homeSP.getFQDN());
- }
- return config;
- }
-
- public HomeSP getHomeSPForConfig(WifiConfiguration config) {
- WifiConfiguration storedConfig = mConfiguredNetworks.getForCurrentUser(config.networkId);
- return storedConfig != null && storedConfig.isPasspoint()
- ? mMOManager.getHomeSP(storedConfig.FQDN)
- : null;
- }
-
- public ScanDetailCache getScanDetailCache(WifiConfiguration config) {
+ /**
+ * 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 = mScanDetailCaches.get(config.networkId);
+ ScanDetailCache cache = getScanDetailCacheForNetwork(config.networkId);
if (cache == null && config.networkId != WifiConfiguration.INVALID_NETWORK_ID) {
- cache = new ScanDetailCache(config);
+ cache = new ScanDetailCache(
+ config, SCAN_CACHE_ENTRIES_MAX_SIZE, SCAN_CACHE_ENTRIES_TRIM_SIZE);
mScanDetailCaches.put(config.networkId, cache);
}
return cache;
}
/**
- * This function run thru the Saved WifiConfigurations and check if some should be linked.
- * @param config
+ * 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.
*/
- public void linkConfiguration(WifiConfiguration config) {
- if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
- mUserManager.getProfiles(mCurrentUserId))) {
- logd("linkConfiguration: Attempting to link config " + config.configKey()
- + " that is not visible to the current user.");
- return;
- }
-
- if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 6) {
- // Ignore configurations with large number of BSSIDs
- return;
- }
- if (!config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
- // Only link WPA_PSK config
- return;
- }
- for (WifiConfiguration link : mConfiguredNetworks.valuesForCurrentUser()) {
- boolean doLink = false;
-
- if (link.configKey().equals(config.configKey())) {
- continue;
- }
-
- if (link.ephemeral) {
- continue;
- }
-
- // Autojoin will be allowed to dynamically jump from a linked configuration
- // to another, hence only link configurations that have equivalent level of security
- if (!link.allowedKeyManagement.equals(config.allowedKeyManagement)) {
- continue;
- }
-
- ScanDetailCache linkedScanDetailCache = getScanDetailCache(link);
- if (linkedScanDetailCache != null && linkedScanDetailCache.size() > 6) {
- // Ignore configurations with large number of BSSIDs
- continue;
- }
-
- 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) {
- logd("linkConfiguration link due to same gw " + link.SSID
- + " and " + config.SSID + " GW " + config.defaultGwMacAddress);
- }
- doLink = true;
- }
- } else {
- // We do not know BOTH default gateways hence we will try to link
- // hoping that WifiConfigurations are indeed behind the same gateway.
- // once both WifiConfiguration have been tried and thus once both efault gateways
- // are known we will revisit the choice of linking them
- if ((getScanDetailCache(config) != null)
- && (getScanDetailCache(config).size() <= 6)) {
-
- for (String abssid : getScanDetailCache(config).keySet()) {
- for (String bbssid : linkedScanDetailCache.keySet()) {
- if (sVVDBG) {
- logd("linkConfiguration try to link due to DBDC BSSID match "
- + link.SSID + " and " + config.SSID + " bssida " + abssid
- + " bssidb " + bbssid);
- }
- if (abssid.regionMatches(true, 0, bbssid, 0, 16)) {
- // If first 16 ascii characters of BSSID matches,
- // we assume this is a DBDC
- doLink = true;
- }
- }
- }
- }
- }
-
- if (doLink && mOnlyLinkSameCredentialConfigurations) {
- String apsk =
- readNetworkVariableFromSupplicantFile(link.configKey(), "psk");
- String bpsk =
- readNetworkVariableFromSupplicantFile(config.configKey(), "psk");
- if (apsk == null || bpsk == null
- || TextUtils.isEmpty(apsk) || TextUtils.isEmpty(apsk)
- || apsk.equals("*") || apsk.equals(DELETED_CONFIG_PSK)
- || !apsk.equals(bpsk)) {
- doLink = false;
- }
- }
-
- if (doLink) {
- if (sVDBG) {
- logd("linkConfiguration: will link " + link.configKey()
- + " and " + config.configKey());
- }
- if (link.linkedConfigurations == null) {
- link.linkedConfigurations = new HashMap<String, Integer>();
- }
- if (config.linkedConfigurations == null) {
- config.linkedConfigurations = new HashMap<String, Integer>();
- }
- if (link.linkedConfigurations.get(config.configKey()) == null) {
- link.linkedConfigurations.put(config.configKey(), Integer.valueOf(1));
- }
- if (config.linkedConfigurations.get(link.configKey()) == null) {
- config.linkedConfigurations.put(link.configKey(), Integer.valueOf(1));
- }
- } else {
- if (link.linkedConfigurations != null
- && (link.linkedConfigurations.get(config.configKey()) != null)) {
- if (sVDBG) {
- logd("linkConfiguration: un-link " + config.configKey()
- + " from " + link.configKey());
- }
- link.linkedConfigurations.remove(config.configKey());
- }
- if (config.linkedConfigurations != null
- && (config.linkedConfigurations.get(link.configKey()) != null)) {
- if (sVDBG) {
- logd("linkConfiguration: un-link " + link.configKey()
- + " from " + config.configKey());
- }
- config.linkedConfigurations.remove(link.configKey());
- }
- }
- }
- }
-
- public HashSet<Integer> makeChannelList(WifiConfiguration config, int age) {
- if (config == null) {
- return null;
- }
- long now_ms = mClock.currentTimeMillis();
-
- HashSet<Integer> channels = new HashSet<Integer>();
-
- //get channels for this configuration, if there are at least 2 BSSIDs
- if (getScanDetailCache(config) == null && config.linkedConfigurations == null) {
- return null;
- }
-
- if (sVDBG) {
- StringBuilder dbg = new StringBuilder();
- dbg.append("makeChannelList age=" + Integer.toString(age)
- + " for " + config.configKey()
- + " max=" + mMaxNumActiveChannelsForPartialScans);
- if (getScanDetailCache(config) != null) {
- dbg.append(" bssids=" + getScanDetailCache(config).size());
- }
- if (config.linkedConfigurations != null) {
- dbg.append(" linked=" + config.linkedConfigurations.size());
- }
- logd(dbg.toString());
- }
-
- int numChannels = 0;
- if (getScanDetailCache(config) != null && getScanDetailCache(config).size() > 0) {
- for (ScanDetail scanDetail : getScanDetailCache(config).values()) {
- ScanResult result = scanDetail.getScanResult();
- //TODO : cout active and passive channels separately
- if (numChannels > mMaxNumActiveChannelsForPartialScans.get()) {
- break;
- }
- if (sVDBG) {
- boolean test = (now_ms - result.seen) < age;
- logd("has " + result.BSSID + " freq=" + Integer.toString(result.frequency)
- + " age=" + Long.toString(now_ms - result.seen) + " ?=" + test);
- }
- if (((now_ms - result.seen) < age)) {
- channels.add(result.frequency);
- numChannels++;
- }
- }
- }
-
- //get channels for linked configurations
- if (config.linkedConfigurations != null) {
- for (String key : config.linkedConfigurations.keySet()) {
- WifiConfiguration linked = getWifiConfiguration(key);
- if (linked == null) {
- continue;
- }
- if (getScanDetailCache(linked) == null) {
- continue;
- }
- for (ScanDetail scanDetail : getScanDetailCache(linked).values()) {
- ScanResult result = scanDetail.getScanResult();
- if (sVDBG) {
- logd("has link: " + result.BSSID
- + " freq=" + Integer.toString(result.frequency)
- + " age=" + Long.toString(now_ms - result.seen));
- }
- if (numChannels > mMaxNumActiveChannelsForPartialScans.get()) {
- break;
- }
- if (((now_ms - result.seen) < age)) {
- channels.add(result.frequency);
- numChannels++;
- }
- }
- }
- }
- return channels;
- }
-
- private Map<HomeSP, PasspointMatch> matchPasspointNetworks(ScanDetail scanDetail) {
- // Nothing to do if no Hotspot 2.0 provider is configured.
- if (!mMOManager.isConfigured()) {
- return null;
- }
- NetworkDetail networkDetail = scanDetail.getNetworkDetail();
- if (!networkDetail.hasInterworking()) {
- return null;
- }
- updateAnqpCache(scanDetail, networkDetail.getANQPElements());
-
- Map<HomeSP, PasspointMatch> matches = matchNetwork(scanDetail, true);
- Log.d(Utils.hs2LogTag(getClass()), scanDetail.getSSID()
- + " pass 1 matches: " + toMatchString(matches));
- return matches;
- }
-
- private Map<HomeSP, PasspointMatch> matchNetwork(ScanDetail scanDetail, boolean query) {
- NetworkDetail networkDetail = scanDetail.getNetworkDetail();
-
- ANQPData anqpData = mAnqpCache.getEntry(networkDetail);
-
- Map<Constants.ANQPElementType, ANQPElement> anqpElements =
- anqpData != null ? anqpData.getANQPElements() : null;
-
- boolean queried = !query;
- Collection<HomeSP> homeSPs = mMOManager.getLoadedSPs().values();
- Map<HomeSP, PasspointMatch> matches = new HashMap<>(homeSPs.size());
- Log.d(Utils.hs2LogTag(getClass()), "match nwk " + scanDetail.toKeyString()
- + ", anqp " + (anqpData != null ? "present" : "missing")
- + ", query " + query + ", home sps: " + homeSPs.size());
-
- for (HomeSP homeSP : homeSPs) {
- PasspointMatch match = homeSP.match(networkDetail, anqpElements, mSIMAccessor);
-
- Log.d(Utils.hs2LogTag(getClass()), " -- "
- + homeSP.getFQDN() + ": match " + match + ", queried " + queried);
-
- if ((match == PasspointMatch.Incomplete || mEnableOsuQueries) && !queried) {
- boolean matchSet = match == PasspointMatch.Incomplete;
- boolean osu = mEnableOsuQueries;
- List<Constants.ANQPElementType> querySet =
- ANQPFactory.buildQueryList(networkDetail, matchSet, osu);
- if (networkDetail.queriable(querySet)) {
- querySet = mAnqpCache.initiate(networkDetail, querySet);
- if (querySet != null) {
- mSupplicantBridge.startANQP(scanDetail, querySet);
- }
- }
- queried = true;
- }
- matches.put(homeSP, match);
- }
- return matches;
- }
-
- public Map<Constants.ANQPElementType, ANQPElement> getANQPData(NetworkDetail network) {
- ANQPData data = mAnqpCache.getEntry(network);
- return data != null ? data.getANQPElements() : null;
- }
-
- public SIMAccessor getSIMAccessor() {
- return mSIMAccessor;
- }
-
- public void notifyANQPDone(Long bssid, boolean success) {
- mSupplicantBridge.notifyANQPDone(bssid, success);
- }
-
- public void notifyIconReceived(IconEvent iconEvent) {
- Intent intent = new Intent(WifiManager.PASSPOINT_ICON_RECEIVED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_BSSID, iconEvent.getBSSID());
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_FILE, iconEvent.getFileName());
- try {
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_ICON_DATA,
- mSupplicantBridge.retrieveIcon(iconEvent));
- } catch (IOException ioe) {
- /* Simply omit the icon data as a failure indication */
- }
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-
- }
-
- private void updateAnqpCache(ScanDetail scanDetail,
- Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
- NetworkDetail networkDetail = scanDetail.getNetworkDetail();
-
- if (anqpElements == null) {
- // Try to pull cached data if query failed.
- ANQPData data = mAnqpCache.getEntry(networkDetail);
- if (data != null) {
- scanDetail.propagateANQPInfo(data.getANQPElements());
- }
- return;
- }
-
- mAnqpCache.update(networkDetail, anqpElements);
- }
-
- private static String toMatchString(Map<HomeSP, PasspointMatch> matches) {
- StringBuilder sb = new StringBuilder();
- for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
- sb.append(' ').append(entry.getKey().getFQDN()).append("->").append(entry.getValue());
- }
- return sb.toString();
- }
-
- private void cacheScanResultForPasspointConfigs(ScanDetail scanDetail,
- Map<HomeSP, PasspointMatch> matches,
- List<WifiConfiguration> associatedWifiConfigurations) {
-
- for (Map.Entry<HomeSP, PasspointMatch> entry : matches.entrySet()) {
- PasspointMatch match = entry.getValue();
- if (match == PasspointMatch.HomeProvider || match == PasspointMatch.RoamingProvider) {
- WifiConfiguration config = getWifiConfigForHomeSP(entry.getKey());
- if (config != null) {
- cacheScanResultForConfig(config, scanDetail, entry.getValue());
- if (associatedWifiConfigurations != null) {
- associatedWifiConfigurations.add(config);
- }
- } else {
- Log.w(Utils.hs2LogTag(getClass()), "Failed to find config for '"
- + entry.getKey().getFQDN() + "'");
- /* perhaps the configuration was deleted?? */
- }
- }
- }
- }
-
- private void cacheScanResultForConfig(
- WifiConfiguration config, ScanDetail scanDetail, PasspointMatch passpointMatch) {
-
+ private void saveToScanDetailCacheForNetwork(
+ WifiConfiguration config, ScanDetail scanDetail) {
ScanResult scanResult = scanDetail.getScanResult();
- ScanDetailCache scanDetailCache = getScanDetailCache(config);
+ ScanDetailCache scanDetailCache = getOrCreateScanDetailCacheForNetwork(config);
if (scanDetailCache == null) {
- Log.w(TAG, "Could not allocate scan cache for " + config.SSID);
+ Log.e(TAG, "Could not allocate scan cache for " + config.getPrintableSsid());
return;
}
@@ -2647,671 +1869,462 @@
scanResult.blackListTimestamp = result.blackListTimestamp;
scanResult.numIpConfigFailures = result.numIpConfigFailures;
scanResult.numConnection = result.numConnection;
- scanResult.isAutoJoinCandidate = result.isAutoJoinCandidate;
}
-
if (config.ephemeral) {
// For an ephemeral Wi-Fi config, the ScanResult should be considered
// untrusted.
scanResult.untrusted = true;
}
- if (scanDetailCache.size() > (MAX_NUM_SCAN_CACHE_ENTRIES + 64)) {
- long now_dbg = 0;
- if (sVVDBG) {
- logd(" Will trim config " + config.configKey()
- + " size " + scanDetailCache.size());
+ // Add the scan detail to this network's scan detail cache.
+ scanDetailCache.put(scanDetail);
- 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));
- } else {
- scanDetailCache.put(scanDetail);
- }
-
- // Since we added a scan result to this configuration, re-attempt linking
- linkConfiguration(config);
- }
-
- private boolean isEncryptionWep(String encryption) {
- return encryption.contains("WEP");
- }
-
- private boolean isEncryptionPsk(String encryption) {
- return encryption.contains("PSK");
- }
-
- private boolean isEncryptionEap(String encryption) {
- return encryption.contains("EAP");
- }
-
- public boolean isOpenNetwork(String encryption) {
- if (!isEncryptionWep(encryption) && !isEncryptionPsk(encryption)
- && !isEncryptionEap(encryption)) {
- return true;
- }
- return false;
- }
-
- public boolean isOpenNetwork(ScanResult scan) {
- String scanResultEncrypt = scan.capabilities;
- return isOpenNetwork(scanResultEncrypt);
- }
-
- public boolean isOpenNetwork(WifiConfiguration config) {
- String configEncrypt = config.configKey();
- return isOpenNetwork(configEncrypt);
+ // Since we added a scan result to this configuration, re-attempt linking.
+ // TODO: Do we really need to do this after every scan result?
+ attemptNetworkLinking(config);
}
/**
- * Get saved WifiConfiguration associated with a scan detail.
- * @param scanDetail input a scanDetail from the scan result
- * @return WifiConfiguration WifiConfiguration associated with this scanDetail, null if none
+ * 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.
*/
- public List<WifiConfiguration> getSavedNetworkFromScanDetail(ScanDetail scanDetail) {
+ private WifiConfiguration getSavedNetworkForScanDetail(ScanDetail scanDetail) {
ScanResult scanResult = scanDetail.getScanResult();
if (scanResult == null) {
+ Log.e(TAG, "No scan result found in scan detail");
return null;
}
- List<WifiConfiguration> savedWifiConfigurations = new ArrayList<>();
- String ssid = "\"" + scanResult.SSID + "\"";
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- if (config.SSID == null || !config.SSID.equals(ssid)) {
- continue;
- }
- if (DBG) {
- localLog("getSavedNetworkFromScanDetail(): try " + config.configKey()
- + " SSID=" + config.SSID + " " + scanResult.SSID + " "
- + scanResult.capabilities);
- }
- String scanResultEncrypt = scanResult.capabilities;
- String configEncrypt = config.configKey();
- if (isEncryptionWep(scanResultEncrypt) && isEncryptionWep(configEncrypt)
- || (isEncryptionPsk(scanResultEncrypt) && isEncryptionPsk(configEncrypt))
- || (isEncryptionEap(scanResultEncrypt) && isEncryptionEap(configEncrypt))
- || (isOpenNetwork(scanResultEncrypt) && isOpenNetwork(configEncrypt))) {
- savedWifiConfigurations.add(config);
+ for (WifiConfiguration config : getInternalConfiguredNetworks()) {
+ if (ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config)) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "getSavedNetworkFromScanDetail Found " + config.configKey()
+ + " for " + scanResult.SSID + "[" + scanResult.capabilities + "]");
+ }
+ return config;
}
}
- return savedWifiConfigurations;
+ return null;
}
/**
- * Create a mapping between the scandetail and the Wificonfiguration it associated with
- * because Passpoint, one BSSID can associated with multiple SSIDs
+ * 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
- * @param isConnectingOrConnected input a boolean to indicate if WiFi is connecting or conncted
- * This is used for avoiding ANQP request
- * @return List<WifiConfiguration> a list of WifiConfigurations associated to this scanDetail
+ * @return WifiConfiguration object representing the network corresponding to the scanDetail,
+ * null if none exists.
*/
- public List<WifiConfiguration> updateSavedNetworkWithNewScanDetail(ScanDetail scanDetail,
- boolean isConnectingOrConnected) {
- ScanResult scanResult = scanDetail.getScanResult();
- if (scanResult == null) {
+ public WifiConfiguration getSavedNetworkForScanDetailAndCache(ScanDetail scanDetail) {
+ WifiConfiguration network = getSavedNetworkForScanDetail(scanDetail);
+ if (network == null) {
return null;
}
- NetworkDetail networkDetail = scanDetail.getNetworkDetail();
- List<WifiConfiguration> associatedWifiConfigurations = new ArrayList<>();
- if (networkDetail.hasInterworking() && !isConnectingOrConnected) {
- Map<HomeSP, PasspointMatch> matches = matchPasspointNetworks(scanDetail);
- if (matches != null) {
- cacheScanResultForPasspointConfigs(scanDetail, matches,
- associatedWifiConfigurations);
- //Do not return here. A BSSID can belong to both passpoint network and non-passpoint
- //Network
- }
+ saveToScanDetailCacheForNetwork(network, scanDetail);
+ // 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
+ // Frame), these scanResult DTIM's are negative and ignored.
+ // Used for metrics collection.
+ if (scanDetail.getNetworkDetail() != null
+ && scanDetail.getNetworkDetail().getDtimInterval() > 0) {
+ network.dtimInterval = scanDetail.getNetworkDetail().getDtimInterval();
}
- List<WifiConfiguration> savedConfigurations = getSavedNetworkFromScanDetail(scanDetail);
- if (savedConfigurations != null) {
- for (WifiConfiguration config : savedConfigurations) {
- cacheScanResultForConfig(config, scanDetail, null);
- associatedWifiConfigurations.add(config);
+ return createExternalWifiConfiguration(network, true);
+ }
+
+ /**
+ * Update the scan detail cache associated with current connected network with latest
+ * RSSI value in the provided WifiInfo.
+ * This is invoked when we get an RSSI poll update after connection.
+ *
+ * @param info WifiInfo instance pointing to the current connected network.
+ */
+ public void updateScanDetailCacheFromWifiInfo(WifiInfo info) {
+ WifiConfiguration config = getInternalConfiguredNetwork(info.getNetworkId());
+ ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(info.getNetworkId());
+ if (config != null && scanDetailCache != null) {
+ ScanDetail scanDetail = scanDetailCache.getScanDetail(info.getBSSID());
+ if (scanDetail != null) {
+ ScanResult result = scanDetail.getScanResult();
+ long previousSeen = result.seen;
+ int previousRssi = result.level;
+ // Update the scan result
+ scanDetail.setSeen();
+ result.level = info.getRssi();
+ // Average the RSSI value
+ result.averageRssi(previousRssi, previousSeen, SCAN_RESULT_MAXIMUM_AGE_MS);
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Updating scan detail cache freq=" + result.frequency
+ + " BSSID=" + result.BSSID
+ + " RSSI=" + result.level
+ + " for " + config.configKey());
+ }
}
}
- if (associatedWifiConfigurations.size() == 0) {
- return null;
- } else {
- return associatedWifiConfigurations;
- }
}
/**
- * Handles the switch to a different foreground user:
- * - Removes all ephemeral networks
- * - Disables private network configurations belonging to the previous foreground user
- * - Enables private network configurations belonging to the new foreground user
+ * Save the ScanDetail to the ScanDetailCache of the given network. This is used
+ * by {@link com.android.server.wifi.hotspot2.PasspointNetworkEvaluator} for caching
+ * ScanDetail for newly created {@link WifiConfiguration} for Passpoint network.
*
- * @param userId The identifier of the new foreground user, after the switch.
- *
- * TODO(b/26785736): Terminate background users if the new foreground user has one or more
- * private network configurations.
+ * @param networkId The ID of the network to save ScanDetail to
+ * @param scanDetail The ScanDetail to cache
*/
- public void handleUserSwitch(int userId) {
- mCurrentUserId = userId;
- Set<WifiConfiguration> ephemeralConfigs = new HashSet<>();
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- if (config.ephemeral) {
- ephemeralConfigs.add(config);
- }
- }
- if (!ephemeralConfigs.isEmpty()) {
- for (WifiConfiguration config : ephemeralConfigs) {
- removeConfigWithoutBroadcast(config);
- }
- saveConfig();
- writeKnownNetworkHistory();
- }
-
- final List<WifiConfiguration> hiddenConfigurations =
- mConfiguredNetworks.handleUserSwitch(mCurrentUserId);
- for (WifiConfiguration network : hiddenConfigurations) {
- disableNetworkNative(network);
- }
- enableAllNetworks();
-
- // TODO(b/26785746): This broadcast is unnecessary if either of the following is true:
- // * The user switch did not change the list of visible networks
- // * The user switch revealed additional networks that were temporarily disabled and got
- // re-enabled now (because enableAllNetworks() sent the same broadcast already).
- sendConfiguredNetworksChangedBroadcast();
- }
-
- public int getCurrentUserId() {
- return mCurrentUserId;
- }
-
- public boolean isCurrentUserProfile(int userId) {
- if (userId == mCurrentUserId) {
- return true;
- }
- final UserInfo parent = mUserManager.getProfileParent(userId);
- return parent != null && parent.id == mCurrentUserId;
- }
-
- /* Compare current and new configuration and write to file on change */
- private NetworkUpdateResult writeIpAndProxyConfigurationsOnChange(
- WifiConfiguration currentConfig,
- WifiConfiguration newConfig,
- boolean isNewNetwork) {
- boolean ipChanged = false;
- boolean proxyChanged = false;
-
- switch (newConfig.getIpAssignment()) {
- case STATIC:
- if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
- ipChanged = true;
- } else {
- ipChanged = !Objects.equals(
- currentConfig.getStaticIpConfiguration(),
- newConfig.getStaticIpConfiguration());
- }
- break;
- case DHCP:
- if (currentConfig.getIpAssignment() != newConfig.getIpAssignment()) {
- ipChanged = true;
- }
- break;
- case UNASSIGNED:
- /* Ignore */
- break;
- default:
- loge("Ignore invalid ip assignment during write");
- break;
- }
-
- switch (newConfig.getProxySettings()) {
- case STATIC:
- case PAC:
- ProxyInfo newHttpProxy = newConfig.getHttpProxy();
- ProxyInfo currentHttpProxy = currentConfig.getHttpProxy();
-
- if (newHttpProxy != null) {
- proxyChanged = !newHttpProxy.equals(currentHttpProxy);
- } else {
- proxyChanged = (currentHttpProxy != null);
- }
- break;
- case NONE:
- if (currentConfig.getProxySettings() != newConfig.getProxySettings()) {
- proxyChanged = true;
- }
- break;
- case UNASSIGNED:
- /* Ignore */
- break;
- default:
- loge("Ignore invalid proxy configuration during write");
- break;
- }
-
- if (ipChanged) {
- currentConfig.setIpAssignment(newConfig.getIpAssignment());
- currentConfig.setStaticIpConfiguration(newConfig.getStaticIpConfiguration());
- log("IP config changed SSID = " + currentConfig.SSID);
- if (currentConfig.getStaticIpConfiguration() != null) {
- log(" static configuration: "
- + currentConfig.getStaticIpConfiguration().toString());
- }
- }
-
- if (proxyChanged) {
- currentConfig.setProxySettings(newConfig.getProxySettings());
- currentConfig.setHttpProxy(newConfig.getHttpProxy());
- log("proxy changed SSID = " + currentConfig.SSID);
- if (currentConfig.getHttpProxy() != null) {
- log(" proxyProperties: " + currentConfig.getHttpProxy().toString());
- }
- }
-
- if (ipChanged || proxyChanged || isNewNetwork) {
- if (sVDBG) {
- logd("writeIpAndProxyConfigurationsOnChange: " + currentConfig.SSID + " -> "
- + newConfig.SSID + " path: " + IP_CONFIG_FILE);
- }
- writeIpAndProxyConfigurations();
- }
- return new NetworkUpdateResult(ipChanged, proxyChanged);
- }
-
- /**
- * Read the variables from the supplicant daemon that are needed to
- * fill in the WifiConfiguration object.
- *
- * @param config the {@link WifiConfiguration} object to be filled in.
- */
- private void readNetworkVariables(WifiConfiguration config) {
- mWifiConfigStore.readNetworkVariables(config);
- }
-
- /* return the allowed key management based on a scan result */
-
- public WifiConfiguration wifiConfigurationFromScanResult(ScanResult result) {
-
- WifiConfiguration config = new WifiConfiguration();
-
- config.SSID = "\"" + result.SSID + "\"";
-
- if (sVDBG) {
- logd("WifiConfiguration from scan results "
- + config.SSID + " cap " + result.capabilities);
- }
-
- if (result.capabilities.contains("PSK") || result.capabilities.contains("EAP")
- || result.capabilities.contains("WEP")) {
- if (result.capabilities.contains("PSK")) {
- config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
- }
-
- if (result.capabilities.contains("EAP")) {
- config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
- config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
- }
-
- if (result.capabilities.contains("WEP")) {
- config.allowedKeyManagement.set(KeyMgmt.NONE);
- config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
- config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
- }
- } else {
- config.allowedKeyManagement.set(KeyMgmt.NONE);
- }
-
- return config;
- }
-
- public WifiConfiguration wifiConfigurationFromScanResult(ScanDetail scanDetail) {
- ScanResult result = scanDetail.getScanResult();
- return wifiConfigurationFromScanResult(result);
- }
-
- /* Returns a unique for a given configuration */
- private static int configKey(WifiConfiguration config) {
- String key = config.configKey();
- return key.hashCode();
- }
-
- void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("Dump of WifiConfigManager");
- pw.println("mLastPriority " + mLastPriority);
- pw.println("Configured networks");
- for (WifiConfiguration conf : getAllConfiguredNetworks()) {
- pw.println(conf);
- }
- pw.println();
- if (mLostConfigsDbg != null && mLostConfigsDbg.size() > 0) {
- pw.println("LostConfigs: ");
- for (String s : mLostConfigsDbg) {
- pw.println(s);
- }
- }
-
- if (mMOManager.isConfigured()) {
- pw.println("Begin dump of ANQP Cache");
- mAnqpCache.dump(pw);
- pw.println("End dump of ANQP Cache");
- }
- }
-
- public String getConfigFile() {
- return IP_CONFIG_FILE;
- }
-
- protected void logd(String s) {
- Log.d(TAG, s);
- }
-
- protected void loge(String s) {
- loge(s, false);
- }
-
- protected void loge(String s, boolean stack) {
- if (stack) {
- Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
- + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
- + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
- + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
- } else {
- Log.e(TAG, s);
- }
- }
-
- private void logKernelTime() {
- long kernelTimeMs = System.nanoTime() / (1000 * 1000);
- StringBuilder builder = new StringBuilder();
- builder.append("kernel time = ")
- .append(kernelTimeMs / 1000)
- .append(".")
- .append(kernelTimeMs % 1000)
- .append("\n");
- localLog(builder.toString());
- }
-
- protected void log(String s) {
- Log.d(TAG, s);
- }
-
- private void localLog(String s) {
- if (mLocalLog != null) {
- mLocalLog.log(s);
- }
- }
-
- private void localLogAndLogcat(String s) {
- localLog(s);
- Log.d(TAG, s);
- }
-
- private void localLogNetwork(String s, int netId) {
- if (mLocalLog == null) {
+ public void updateScanDetailForNetwork(int networkId, ScanDetail scanDetail) {
+ WifiConfiguration network = getInternalConfiguredNetwork(networkId);
+ if (network == null) {
return;
}
-
- WifiConfiguration config;
- synchronized (mConfiguredNetworks) { // !!! Useless synchronization
- config = mConfiguredNetworks.getForAllUsers(netId);
- }
-
- if (config != null) {
- mLocalLog.log(s + " " + config.getPrintableSsid() + " " + netId
- + " status=" + config.status
- + " key=" + config.configKey());
- } else {
- mLocalLog.log(s + " " + netId);
- }
+ saveToScanDetailCacheForNetwork(network, scanDetail);
}
- 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");
+ /**
+ * Helper method to check if the 2 provided networks can be linked or not.
+ * Networks are considered for linking if:
+ * 1. Share the same GW MAC address.
+ * 2. Scan results for the networks have AP's with MAC address which differ only in the last
+ * nibble.
+ *
+ * @param network1 WifiConfiguration corresponding to network 1.
+ * @param network2 WifiConfiguration corresponding to network 2.
+ * @param scanDetailCache1 ScanDetailCache entry for network 1.
+ * @param scanDetailCache1 ScanDetailCache entry for network 2.
+ * @return true if the networks should be linked, false if the networks should be unlinked.
+ */
+ private boolean shouldNetworksBeLinked(
+ WifiConfiguration network1, WifiConfiguration network2,
+ ScanDetailCache scanDetailCache1, ScanDetailCache scanDetailCache2) {
+ // TODO (b/30706406): Link networks only with same passwords if the
+ // |mOnlyLinkSameCredentialConfigurations| flag is set.
+ if (mOnlyLinkSameCredentialConfigurations) {
+ if (!TextUtils.equals(network1.preSharedKey, network2.preSharedKey)) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "shouldNetworksBeLinked unlink due to password mismatch");
}
- } else {
- Slog.e(TAG, "Could not load client certificate " + Credentials
- .USER_CERTIFICATE + client);
- mNeedsSoftwareKeystore = true;
+ return false;
}
-
- } catch(CertificateException e) {
- Slog.e(TAG, "Could not read certificates");
- mCaCert = null;
- mClientCertificate = null;
}
- */
-
+ if (network1.defaultGwMacAddress != null && network2.defaultGwMacAddress != null) {
+ // If both default GW are known, link only if they are equal
+ if (network1.defaultGwMacAddress.equals(network2.defaultGwMacAddress)) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "shouldNetworksBeLinked link due to same gw " + network2.SSID
+ + " and " + network1.SSID + " GW " + network1.defaultGwMacAddress);
+ }
+ return true;
+ }
+ } else {
+ // We do not know BOTH default gateways hence we will try to link
+ // hoping that WifiConfigurations are indeed behind the same gateway.
+ // once both WifiConfiguration have been tried and thus once both default gateways
+ // are known we will revisit the choice of linking them.
+ if (scanDetailCache1 != null && scanDetailCache2 != null) {
+ for (String abssid : scanDetailCache1.keySet()) {
+ for (String bbssid : scanDetailCache2.keySet()) {
+ if (abssid.regionMatches(
+ true, 0, bbssid, 0, LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) {
+ // If first 16 ASCII characters of BSSID matches,
+ // we assume this is a DBDC.
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "shouldNetworksBeLinked link due to DBDC BSSID match "
+ + network2.SSID + " and " + network1.SSID
+ + " bssida " + abssid + " bssidb " + bbssid);
+ }
+ return true;
+ }
+ }
+ }
+ }
+ }
return false;
}
/**
- * Resets all sim networks from the network list.
- */
- public void resetSimNetworks() {
- mWifiConfigStore.resetSimNetworks(mConfiguredNetworks.valuesForCurrentUser());
- }
-
- boolean isNetworkConfigured(WifiConfiguration config) {
- // Check if either we have a network Id or a WifiConfiguration
- // matching the one we are trying to add.
-
- if (config.networkId != INVALID_NETWORK_ID) {
- return (mConfiguredNetworks.getForCurrentUser(config.networkId) != null);
- }
-
- return (mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey()) != null);
- }
-
- /**
- * Checks if uid has access to modify the configuration corresponding to networkId.
+ * Helper methods to link 2 networks together.
*
- * The conditions checked are, in descending priority order:
- * - Disallow modification if the the configuration is not visible to the uid.
- * - Allow modification if the uid represents the Device Owner app.
- * - Allow modification if both of the following are true:
- * - The uid represents the configuration's creator or an app holding OVERRIDE_CONFIG_WIFI.
- * - The modification is only for administrative annotation (e.g. when connecting) or the
- * configuration is not lockdown eligible (which currently means that it was not last
- * updated by the DO).
- * - Allow modification if configuration lockdown is explicitly disabled and the uid represents
- * an app holding OVERRIDE_CONFIG_WIFI.
- * - In all other cases, disallow modification.
+ * @param network1 WifiConfiguration corresponding to network 1.
+ * @param network2 WifiConfiguration corresponding to network 2.
*/
- boolean canModifyNetwork(int uid, int networkId, boolean onlyAnnotate) {
- WifiConfiguration config = mConfiguredNetworks.getForCurrentUser(networkId);
-
- if (config == null) {
- loge("canModifyNetwork: cannot find config networkId " + networkId);
- return false;
+ private void linkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "linkNetworks will link " + network2.configKey()
+ + " and " + network1.configKey());
}
-
- final DevicePolicyManagerInternal dpmi = LocalServices.getService(
- DevicePolicyManagerInternal.class);
-
- final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
- DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-
- if (isUidDeviceOwner) {
- return true;
+ if (network2.linkedConfigurations == null) {
+ network2.linkedConfigurations = new HashMap<>();
}
-
- final boolean isCreator = (config.creatorUid == uid);
-
- if (onlyAnnotate) {
- return isCreator || checkConfigOverridePermission(uid);
+ if (network1.linkedConfigurations == null) {
+ network1.linkedConfigurations = new HashMap<>();
}
-
- // Check if device has DPM capability. If it has and dpmi is still null, then we
- // treat this case with suspicion and bail out.
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)
- && dpmi == null) {
- return false;
- }
-
- // WiFi config lockdown related logic. At this point we know uid NOT to be a Device Owner.
-
- final boolean isConfigEligibleForLockdown = dpmi != null && dpmi.isActiveAdminWithPolicy(
- config.creatorUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- if (!isConfigEligibleForLockdown) {
- return isCreator || checkConfigOverridePermission(uid);
- }
-
- final ContentResolver resolver = mContext.getContentResolver();
- final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
- Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
- return !isLockdownFeatureEnabled && checkConfigOverridePermission(uid);
+ // TODO (b/30638473): This needs to become a set instead of map, but it will need
+ // public interface changes and need some migration of existing store data.
+ network2.linkedConfigurations.put(network1.configKey(), 1);
+ network1.linkedConfigurations.put(network2.configKey(), 1);
}
/**
- * Saves the network and set the candidate.
- * @param config WifiConfiguration to save.
- * @param scanResult ScanResult to be used as the network selection candidate.
- * @return WifiConfiguration that was saved and with the status updated.
+ * Helper methods to unlink 2 networks from each other.
+ *
+ * @param network1 WifiConfiguration corresponding to network 1.
+ * @param network2 WifiConfiguration corresponding to network 2.
*/
- public WifiConfiguration saveNetworkAndSetCandidate(WifiConfiguration config,
- ScanResult scanResult) {
- saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
-
- config.getNetworkSelectionStatus().setCandidate(scanResult);
- return config;
+ private void unlinkNetworks(WifiConfiguration network1, WifiConfiguration network2) {
+ if (network2.linkedConfigurations != null
+ && (network2.linkedConfigurations.get(network1.configKey()) != null)) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "unlinkNetworks un-link " + network1.configKey()
+ + " from " + network2.configKey());
+ }
+ network2.linkedConfigurations.remove(network1.configKey());
+ }
+ if (network1.linkedConfigurations != null
+ && (network1.linkedConfigurations.get(network2.configKey()) != null)) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "unlinkNetworks un-link " + network2.configKey()
+ + " from " + network1.configKey());
+ }
+ network1.linkedConfigurations.remove(network2.configKey());
+ }
}
+ /**
+ * This method runs through all the saved networks and checks if the provided network can be
+ * linked with any of them.
+ *
+ * @param config WifiConfiguration object corresponding to the network that needs to be
+ * checked for potential links.
+ */
+ private void attemptNetworkLinking(WifiConfiguration config) {
+ // Only link WPA_PSK config.
+ if (!config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ return;
+ }
+ ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(config.networkId);
+ // Ignore configurations with large number of BSSIDs.
+ if (scanDetailCache != null
+ && scanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
+ return;
+ }
+ for (WifiConfiguration linkConfig : getInternalConfiguredNetworks()) {
+ if (linkConfig.configKey().equals(config.configKey())) {
+ continue;
+ }
+ if (linkConfig.ephemeral) {
+ continue;
+ }
+ // Network Selector will be allowed to dynamically jump from a linked configuration
+ // to another, hence only link configurations that have WPA_PSK security type.
+ if (!linkConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ continue;
+ }
+ ScanDetailCache linkScanDetailCache =
+ getScanDetailCacheForNetwork(linkConfig.networkId);
+ // Ignore configurations with large number of BSSIDs.
+ if (linkScanDetailCache != null
+ && linkScanDetailCache.size() > LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES) {
+ continue;
+ }
+ // Check if the networks should be linked/unlinked.
+ if (shouldNetworksBeLinked(
+ config, linkConfig, scanDetailCache, linkScanDetailCache)) {
+ linkNetworks(config, linkConfig);
+ } else {
+ unlinkNetworks(config, linkConfig);
+ }
+ }
+ }
/**
- * Get the Scan Result candidate.
- * @param config WifiConfiguration to get status for.
- * @return scanResult which is the selection candidate.
+ * Helper method to fetch list of channels for a network from the associated ScanResult's cache
+ * and add it to the provided channel as long as the size of the set is less than
+ * |maxChannelSetSize|.
+ *
+ * @param channelSet Channel set holding all the channels for the network.
+ * @param scanDetailCache ScanDetailCache entry associated with the network.
+ * @param nowInMillis current timestamp to be used for age comparison.
+ * @param ageInMillis only consider scan details whose timestamps are earlier than this
+ * value.
+ * @param maxChannelSetSize Maximum number of channels to be added to the set.
+ * @return false if the list is full, true otherwise.
*/
- public ScanResult getScanResultCandidate(WifiConfiguration config) {
+ private boolean addToChannelSetForNetworkFromScanDetailCache(
+ Set<Integer> channelSet, ScanDetailCache scanDetailCache,
+ long nowInMillis, long ageInMillis, int maxChannelSetSize) {
+ if (scanDetailCache != null && scanDetailCache.size() > 0) {
+ for (ScanDetail scanDetail : scanDetailCache.values()) {
+ ScanResult result = scanDetail.getScanResult();
+ boolean valid = (nowInMillis - result.seen) < ageInMillis;
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "fetchChannelSetForNetwork has " + result.BSSID + " freq "
+ + result.frequency + " age " + (nowInMillis - result.seen)
+ + " ?=" + valid);
+ }
+ if (valid) {
+ channelSet.add(result.frequency);
+ }
+ if (channelSet.size() >= maxChannelSetSize) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Retrieve a set of channels on which AP's for the provided network was seen using the
+ * internal ScanResult's cache {@link #mScanDetailCaches}. This is used for initiating partial
+ * scans for the currently connected network.
+ *
+ * @param networkId network ID corresponding to the network.
+ * @param ageInMillis only consider scan details whose timestamps are earlier than this value.
+ * @param homeChannelFreq frequency of the currently connected network.
+ * @return Set containing the frequencies on which this network was found, null if the network
+ * was not found or there are no associated scan details in the cache.
+ */
+ public Set<Integer> fetchChannelSetForNetworkForPartialScan(int networkId, long ageInMillis,
+ int homeChannelFreq) {
+ WifiConfiguration config = getInternalConfiguredNetwork(networkId);
if (config == null) {
return null;
}
- return config.getNetworkSelectionStatus().getCandidate();
- }
-
- /**
- * Checks if uid has access to modify config.
- */
- boolean canModifyNetwork(int uid, WifiConfiguration config, boolean onlyAnnotate) {
- if (config == null) {
- loge("canModifyNetowrk recieved null configuration");
- return false;
+ ScanDetailCache scanDetailCache = getScanDetailCacheForNetwork(networkId);
+ if (scanDetailCache == null && config.linkedConfigurations == null) {
+ Log.i(TAG, "No scan detail and linked configs associated with networkId " + networkId);
+ return null;
}
+ if (mVerboseLoggingEnabled) {
+ StringBuilder dbg = new StringBuilder();
+ dbg.append("fetchChannelSetForNetworkForPartialScan ageInMillis ")
+ .append(ageInMillis)
+ .append(" for ")
+ .append(config.configKey())
+ .append(" max ")
+ .append(mMaxNumActiveChannelsForPartialScans);
+ if (scanDetailCache != null) {
+ dbg.append(" bssids " + scanDetailCache.size());
+ }
+ if (config.linkedConfigurations != null) {
+ dbg.append(" linked " + config.linkedConfigurations.size());
+ }
+ Log.v(TAG, dbg.toString());
+ }
+ Set<Integer> channelSet = new HashSet<>();
- // Resolve the correct network id.
- int netid;
- if (config.networkId != INVALID_NETWORK_ID) {
- netid = config.networkId;
- } else {
- WifiConfiguration test =
- mConfiguredNetworks.getByConfigKeyForCurrentUser(config.configKey());
- if (test == null) {
- return false;
- } else {
- netid = test.networkId;
+ // First add the currently connected network channel.
+ if (homeChannelFreq > 0) {
+ channelSet.add(homeChannelFreq);
+ if (channelSet.size() >= mMaxNumActiveChannelsForPartialScans) {
+ return channelSet;
}
}
- return canModifyNetwork(uid, netid, onlyAnnotate);
- }
+ long nowInMillis = mClock.getWallClockMillis();
- boolean checkConfigOverridePermission(int uid) {
- try {
- return (mFacade.checkUidPermission(
- android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid)
- == PackageManager.PERMISSION_GRANTED);
- } catch (RemoteException e) {
- return false;
+ // Then get channels for the network.
+ if (!addToChannelSetForNetworkFromScanDetailCache(
+ channelSet, scanDetailCache, nowInMillis, ageInMillis,
+ mMaxNumActiveChannelsForPartialScans)) {
+ return channelSet;
}
- }
- int getMaxDhcpRetries() {
- return mFacade.getIntegerSetting(mContext,
- Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT,
- DEFAULT_MAX_DHCP_RETRIES);
- }
-
- void clearBssidBlacklist() {
- mWifiConfigStore.clearBssidBlacklist();
- }
-
- void blackListBssid(String bssid) {
- mWifiConfigStore.blackListBssid(bssid);
- }
-
- public boolean isBssidBlacklisted(String bssid) {
- return mWifiConfigStore.isBssidBlacklisted(bssid);
- }
-
- public boolean getEnableAutoJoinWhenAssociated() {
- return mEnableAutoJoinWhenAssociated.get();
- }
-
- public void setEnableAutoJoinWhenAssociated(boolean enabled) {
- mEnableAutoJoinWhenAssociated.set(enabled);
- }
-
- public void setActiveScanDetail(ScanDetail activeScanDetail) {
- synchronized (mActiveScanDetailLock) {
- mActiveScanDetail = activeScanDetail;
+ // Lastly get channels for linked networks.
+ if (config.linkedConfigurations != null) {
+ for (String configKey : config.linkedConfigurations.keySet()) {
+ WifiConfiguration linkedConfig = getInternalConfiguredNetwork(configKey);
+ if (linkedConfig == null) {
+ continue;
+ }
+ ScanDetailCache linkedScanDetailCache =
+ getScanDetailCacheForNetwork(linkedConfig.networkId);
+ if (!addToChannelSetForNetworkFromScanDetailCache(
+ channelSet, linkedScanDetailCache, nowInMillis, ageInMillis,
+ mMaxNumActiveChannelsForPartialScans)) {
+ break;
+ }
+ }
}
+ return channelSet;
+ }
+
+ /**
+ * Retrieves a list of all the saved networks before enabling disconnected/connected PNO.
+ *
+ * PNO network list sent to the firmware has limited size. If there are a lot of saved
+ * networks, this list will be truncated and we might end up not sending the networks
+ * with the highest chance of connecting to the firmware.
+ * So, re-sort the network list based on the frequency of connection to those networks
+ * and whether it was last seen in the scan results.
+ *
+ * TODO (b/30399964): Recalculate the list whenever network status changes.
+ * @return list of networks with updated priorities.
+ */
+ public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() {
+ List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>();
+ List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
+ // Remove any permanently disabled networks.
+ Iterator<WifiConfiguration> iter = networks.iterator();
+ while (iter.hasNext()) {
+ WifiConfiguration config = iter.next();
+ if (config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
+ iter.remove();
+ }
+ }
+ Collections.sort(networks, sScanListComparator);
+ // Let's use the network list size - 1 as the highest priority and then go down from there.
+ // So, the most frequently connected network has the highest priority now.
+ int priority = networks.size() - 1;
+ for (WifiConfiguration config : networks) {
+ pnoList.add(WifiConfigurationUtil.createPnoNetwork(config, priority));
+ priority--;
+ }
+ return pnoList;
+ }
+
+ /**
+ * Retrieves a list of all the saved hidden networks for scans.
+ *
+ * Hidden network list sent to the firmware has limited size. If there are a lot of saved
+ * networks, this list will be truncated and we might end up not sending the networks
+ * with the highest chance of connecting to the firmware.
+ * So, re-sort the network list based on the frequency of connection to those networks
+ * and whether it was last seen in the scan results.
+ *
+ * @return list of networks with updated priorities.
+ */
+ public List<WifiScanner.ScanSettings.HiddenNetwork> retrieveHiddenNetworkList() {
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenList = new ArrayList<>();
+ List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks());
+ // Remove any permanently disabled networks or non hidden networks.
+ Iterator<WifiConfiguration> iter = networks.iterator();
+ while (iter.hasNext()) {
+ WifiConfiguration config = iter.next();
+ if (!config.hiddenSSID ||
+ config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
+ iter.remove();
+ }
+ }
+ Collections.sort(networks, sScanListComparator);
+ // Let's use the network list size - 1 as the highest priority and then go down from there.
+ // So, the most frequently connected network has the highest priority now.
+ int priority = networks.size() - 1;
+ for (WifiConfiguration config : networks) {
+ hiddenList.add(
+ new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
+ priority--;
+ }
+ return hiddenList;
}
/**
* Check if the provided ephemeral network was deleted by the user or not.
+ *
* @param ssid caller must ensure that the SSID passed thru this API match
- * the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
+ * the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
* @return true if network was deleted, false otherwise.
*/
public boolean wasEphemeralNetworkDeleted(String ssid) {
@@ -3319,11 +2332,508 @@
}
/**
- * Check if the User has enabled connecting to carrier networks from Settings.
- * @return true if enabled in Settings, false otherwise.
+ * Disable an ephemeral SSID for the purpose of network selection.
+ *
+ * The only way to "un-disable it" is if the user create a network for that SSID and then
+ * forget it.
+ *
+ * @param ssid caller must ensure that the SSID passed thru this API match
+ * the WifiConfiguration.SSID rules, and thus be surrounded by quotes.
+ * @return the {@link WifiConfiguration} corresponding to this SSID, if any, so that we can
+ * disconnect if this is the current network.
*/
- public boolean getIsCarrierNetworkEnabledByUser() {
- return android.provider.Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_CONNECT_CARRIER_NETWORKS, 0) == 1;
+ public WifiConfiguration disableEphemeralNetwork(String ssid) {
+ if (ssid == null) {
+ return null;
+ }
+ WifiConfiguration foundConfig = null;
+ for (WifiConfiguration config : getInternalConfiguredNetworks()) {
+ if (config.ephemeral && TextUtils.equals(config.SSID, ssid)) {
+ foundConfig = config;
+ break;
+ }
+ }
+ mDeletedEphemeralSSIDs.add(ssid);
+ Log.d(TAG, "Forget ephemeral SSID " + ssid + " num=" + mDeletedEphemeralSSIDs.size());
+ if (foundConfig != null) {
+ Log.d(TAG, "Found ephemeral config in disableEphemeralNetwork: "
+ + foundConfig.networkId);
+ }
+ return foundConfig;
+ }
+
+ /**
+ * Resets all sim networks state.
+ */
+ public void resetSimNetworks() {
+ if (mVerboseLoggingEnabled) localLog("resetSimNetworks");
+ for (WifiConfiguration config : getInternalConfiguredNetworks()) {
+ if (TelephonyUtil.isSimConfig(config)) {
+ String currentIdentity = TelephonyUtil.getSimIdentity(mTelephonyManager, config);
+ // Update the loaded config
+ config.enterpriseConfig.setIdentity(currentIdentity);
+ if (config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.PEAP) {
+ config.enterpriseConfig.setAnonymousIdentity("");
+ }
+ }
+ }
+ }
+
+ /**
+ * Any network using certificates to authenticate access requires unlocked key store; unless
+ * the certificates can be stored with hardware encryption
+ *
+ * @return true if we need an unlocked keystore, false otherwise.
+ */
+ public boolean needsUnlockedKeyStore() {
+ for (WifiConfiguration config : getInternalConfiguredNetworks()) {
+ if (WifiConfigurationUtil.isConfigForEapNetwork(config)) {
+ if (mWifiKeyStore.needsSoftwareBackedKeyStore(config.enterpriseConfig)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Helper method to perform the following operations during user switch/unlock:
+ * - Remove private networks of the old user.
+ * - Load from the new user store file.
+ * - Save the store files again to migrate any user specific networks from the shared store
+ * to user store.
+ * This method assumes the user store is visible (i.e CE storage is unlocked). So, the caller
+ * should ensure that the stores are accessible before invocation.
+ *
+ * @param userId The identifier of the new foreground user, after the unlock or switch.
+ */
+ private void handleUserUnlockOrSwitch(int userId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Loading from store after user switch/unlock for " + userId);
+ }
+ // Switch out the user store file.
+ if (loadFromUserStoreAfterUnlockOrSwitch(userId)) {
+ saveToStore(true);
+ mPendingUnlockStoreRead = false;
+ }
+ }
+
+ /**
+ * Handles the switch to a different foreground user:
+ * - Flush the current state to the old user's store file.
+ * - Switch the user specific store file.
+ * - Reload the networks from the store files (shared & user).
+ * - Write the store files to move any user specific private networks from shared store to user
+ * store.
+ *
+ * Need to be called when {@link com.android.server.SystemService#onSwitchUser(int)} is invoked.
+ *
+ * @param userId The identifier of the new foreground user, after the switch.
+ * @return List of network ID's of all the private networks of the old user which will be
+ * removed from memory.
+ */
+ public Set<Integer> handleUserSwitch(int userId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Handling user switch for " + userId);
+ }
+ if (userId == mCurrentUserId) {
+ Log.w(TAG, "User already in foreground " + userId);
+ return new HashSet<>();
+ }
+ if (mPendingStoreRead) {
+ Log.wtf(TAG, "Unexpected user switch before store is read!");
+ return new HashSet<>();
+ }
+ if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
+ saveToStore(true);
+ }
+ // Remove any private networks of the old user before switching the userId.
+ Set<Integer> removedNetworkIds = clearInternalUserData(mCurrentUserId);
+ mConfiguredNetworks.setNewUser(userId);
+ mCurrentUserId = userId;
+
+ if (mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
+ handleUserUnlockOrSwitch(mCurrentUserId);
+ } else {
+ // Cannot read data from new user's CE store file before they log-in.
+ mPendingUnlockStoreRead = true;
+ Log.i(TAG, "Waiting for user unlock to load from store");
+ }
+ return removedNetworkIds;
+ }
+
+ /**
+ * Handles the unlock of foreground user. This maybe needed to read the store file if the user's
+ * CE storage is not visible when {@link #handleUserSwitch(int)} is invoked.
+ *
+ * Need to be called when {@link com.android.server.SystemService#onUnlockUser(int)} is invoked.
+ *
+ * @param userId The identifier of the user that unlocked.
+ */
+ public void handleUserUnlock(int userId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Handling user unlock for " + userId);
+ }
+ if (mPendingStoreRead) {
+ Log.w(TAG, "Ignore user unlock until store is read!");
+ mDeferredUserUnlockRead = true;
+ return;
+ }
+ if (userId == mCurrentUserId && mPendingUnlockStoreRead) {
+ handleUserUnlockOrSwitch(mCurrentUserId);
+ }
+ }
+
+ /**
+ * Handles the stop of foreground user. This is needed to write the store file to flush
+ * out any pending data before the user's CE store storage is unavailable.
+ *
+ * Need to be called when {@link com.android.server.SystemService#onStopUser(int)} is invoked.
+ *
+ * @param userId The identifier of the user that stopped.
+ */
+ public void handleUserStop(int userId) {
+ if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) {
+ saveToStore(true);
+ clearInternalData();
+ mCurrentUserId = UserHandle.USER_SYSTEM;
+ }
+ }
+
+ /**
+ * Helper method to clear internal databases.
+ * This method clears the:
+ * - List of configured networks.
+ * - Map of scan detail caches.
+ * - List of deleted ephemeral networks.
+ */
+ private void clearInternalData() {
+ mConfiguredNetworks.clear();
+ mDeletedEphemeralSSIDs.clear();
+ mScanDetailCaches.clear();
+ clearLastSelectedNetwork();
+ }
+
+ /**
+ * Helper method to clear internal databases of the specified user.
+ * This method clears the:
+ * - Private configured configured networks of the specified user.
+ * - Map of scan detail caches.
+ * - List of deleted ephemeral networks.
+ *
+ * @param userId The identifier of the current foreground user, before the switch.
+ * @return List of network ID's of all the private networks of the old user which will be
+ * removed from memory.
+ */
+ private Set<Integer> clearInternalUserData(int userId) {
+ Set<Integer> removedNetworkIds = new HashSet<>();
+ // Remove any private networks of the old user before switching the userId.
+ for (WifiConfiguration config : getInternalConfiguredNetworks()) {
+ if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile(
+ config.creatorUid, mUserManager.getProfiles(userId))) {
+ removedNetworkIds.add(config.networkId);
+ mConfiguredNetworks.remove(config.networkId);
+ }
+ }
+ mDeletedEphemeralSSIDs.clear();
+ mScanDetailCaches.clear();
+ clearLastSelectedNetwork();
+ return removedNetworkIds;
+ }
+
+ /**
+ * Helper function to populate the internal (in-memory) data from the retrieved shared store
+ * (file) data.
+ *
+ * @param configurations list of configurations retrieved from store.
+ */
+ private void loadInternalDataFromSharedStore(
+ List<WifiConfiguration> configurations) {
+ for (WifiConfiguration configuration : configurations) {
+ configuration.networkId = mNextNetworkId++;
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Adding network from shared store " + configuration.configKey());
+ }
+ mConfiguredNetworks.put(configuration);
+ }
+ }
+
+ /**
+ * Helper function to populate the internal (in-memory) data from the retrieved user store
+ * (file) data.
+ *
+ * @param configurations list of configurations retrieved from store.
+ * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
+ * the user.
+ */
+ private void loadInternalDataFromUserStore(
+ List<WifiConfiguration> configurations, Set<String> deletedEphemeralSSIDs) {
+ for (WifiConfiguration configuration : configurations) {
+ configuration.networkId = mNextNetworkId++;
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "Adding network from user store " + configuration.configKey());
+ }
+ mConfiguredNetworks.put(configuration);
+ }
+ for (String ssid : deletedEphemeralSSIDs) {
+ mDeletedEphemeralSSIDs.add(ssid);
+ }
+ }
+
+ /**
+ * Helper function to populate the internal (in-memory) data from the retrieved stores (file)
+ * data.
+ * This method:
+ * 1. Clears all existing internal data.
+ * 2. Sends out the networks changed broadcast after loading all the data.
+ *
+ * @param sharedConfigurations list of network configurations retrieved from shared store.
+ * @param userConfigurations list of network configurations retrieved from user store.
+ * @param deletedEphemeralSSIDs list of ssid's representing the ephemeral networks deleted by
+ * the user.
+ */
+ private void loadInternalData(
+ List<WifiConfiguration> sharedConfigurations,
+ List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSSIDs) {
+ // Clear out all the existing in-memory lists and load the lists from what was retrieved
+ // from the config store.
+ clearInternalData();
+ loadInternalDataFromSharedStore(sharedConfigurations);
+ loadInternalDataFromUserStore(userConfigurations, deletedEphemeralSSIDs);
+ if (mConfiguredNetworks.sizeForAllUsers() == 0) {
+ Log.w(TAG, "No stored networks found.");
+ }
+ sendConfiguredNetworksChangedBroadcast();
+ mPendingStoreRead = false;
+ }
+
+ /**
+ * Migrate data from legacy store files. The function performs the following operations:
+ * 1. Check if the legacy store files are present.
+ * 2. If yes, read all the data from the store files.
+ * 3. Save it to the new store files.
+ * 4. Delete the legacy store file.
+ *
+ * @return true if migration was successful or not needed (fresh install), false if it failed.
+ */
+ public boolean migrateFromLegacyStore() {
+ if (!mWifiConfigStoreLegacy.areStoresPresent()) {
+ Log.d(TAG, "Legacy store files not found. No migration needed!");
+ return true;
+ }
+ WifiConfigStoreDataLegacy storeData = mWifiConfigStoreLegacy.read();
+ Log.d(TAG, "Reading from legacy store completed");
+ loadInternalData(storeData.getConfigurations(), new ArrayList<WifiConfiguration>(),
+ storeData.getDeletedEphemeralSSIDs());
+
+ // Setup user store for the current user in case it have not setup yet, so that data
+ // owned by the current user will be backed to the user store.
+ if (mDeferredUserUnlockRead) {
+ mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId));
+ mDeferredUserUnlockRead = false;
+ }
+
+ if (!saveToStore(true)) {
+ return false;
+ }
+ mWifiConfigStoreLegacy.removeStores();
+ Log.d(TAG, "Migration from legacy store completed");
+ return true;
+ }
+
+ /**
+ * Read the config store and load the in-memory lists from the store data retrieved and sends
+ * out the networks changed broadcast.
+ *
+ * This reads all the network configurations from:
+ * 1. Shared WifiConfigStore.xml
+ * 2. User WifiConfigStore.xml
+ *
+ * @return true on success or not needed (fresh install/pending legacy store migration),
+ * false otherwise.
+ */
+ public boolean loadFromStore() {
+ if (!mWifiConfigStore.areStoresPresent()) {
+ Log.d(TAG, "New store files not found. No saved networks loaded!");
+ if (!mWifiConfigStoreLegacy.areStoresPresent()) {
+ // No legacy store files either, so reset the pending store read flag.
+ mPendingStoreRead = false;
+ }
+ return true;
+ }
+ // If the user unlock comes in before we load from store, which means the user store have
+ // not been setup yet for the current user. Setup the user store before the read so that
+ // configurations for the current user will also being loaded.
+ if (mDeferredUserUnlockRead) {
+ Log.i(TAG, "Handling user unlock before loading from store.");
+ mWifiConfigStore.setUserStore(WifiConfigStore.createUserFile(mCurrentUserId));
+ mDeferredUserUnlockRead = false;
+ }
+ try {
+ mWifiConfigStore.read();
+ } catch (IOException e) {
+ Log.wtf(TAG, "Reading from new store failed. All saved networks are lost!", e);
+ return false;
+ } catch (XmlPullParserException e) {
+ Log.wtf(TAG, "XML deserialization of store failed. All saved networks are lost!", e);
+ return false;
+ }
+ loadInternalData(mNetworkListStoreData.getSharedConfigurations(),
+ mNetworkListStoreData.getUserConfigurations(),
+ mDeletedEphemeralSsidsStoreData.getSsidList());
+ return true;
+ }
+
+ /**
+ * Read the user config store and load the in-memory lists from the store data retrieved and
+ * sends out the networks changed broadcast.
+ * This should be used for all user switches/unlocks to only load networks from the user
+ * specific store and avoid reloading the shared networks.
+ *
+ * This reads all the network configurations from:
+ * 1. User WifiConfigStore.xml
+ *
+ * @param userId The identifier of the foreground user.
+ * @return true on success, false otherwise.
+ */
+ public boolean loadFromUserStoreAfterUnlockOrSwitch(int userId) {
+ try {
+ mWifiConfigStore.switchUserStoreAndRead(WifiConfigStore.createUserFile(userId));
+ } catch (IOException e) {
+ Log.wtf(TAG, "Reading from new store failed. All saved private networks are lost!", e);
+ return false;
+ } catch (XmlPullParserException e) {
+ Log.wtf(TAG, "XML deserialization of store failed. All saved private networks are" +
+ "lost!", e);
+ return false;
+ }
+ loadInternalDataFromUserStore(mNetworkListStoreData.getUserConfigurations(),
+ mDeletedEphemeralSsidsStoreData.getSsidList());
+ return true;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean saveToStore(boolean forceWrite) {
+ ArrayList<WifiConfiguration> sharedConfigurations = new ArrayList<>();
+ ArrayList<WifiConfiguration> userConfigurations = new ArrayList<>();
+ // List of network IDs for legacy Passpoint configuration to be removed.
+ List<Integer> legacyPasspointNetId = new ArrayList<>();
+ for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
+ // Ignore ephemeral networks and non-legacy Passpoint configurations.
+ if (config.ephemeral || (config.isPasspoint() && !config.isLegacyPasspointConfig)) {
+ continue;
+ }
+
+ // Migrate the legacy Passpoint configurations owned by the current user to
+ // {@link PasspointManager}.
+ if (config.isLegacyPasspointConfig && WifiConfigurationUtil.doesUidBelongToAnyProfile(
+ config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) {
+ legacyPasspointNetId.add(config.networkId);
+ // Migrate the legacy Passpoint configuration and add it to PasspointManager.
+ if (!PasspointManager.addLegacyPasspointConfig(config)) {
+ Log.e(TAG, "Failed to migrate legacy Passpoint config: " + config.FQDN);
+ }
+ // This will prevent adding |config| to the |sharedConfigurations|.
+ continue;
+ }
+
+ // We push all shared networks & private networks not belonging to the current
+ // user to the shared store. Ideally, private networks for other users should
+ // not even be in memory,
+ // But, this logic is in place to deal with store migration from N to O
+ // because all networks were previously stored in a central file. We cannot
+ // write these private networks to the user specific store until the corresponding
+ // user logs in.
+ if (config.shared || !WifiConfigurationUtil.doesUidBelongToAnyProfile(
+ config.creatorUid, mUserManager.getProfiles(mCurrentUserId))) {
+ sharedConfigurations.add(config);
+ } else {
+ userConfigurations.add(config);
+ }
+ }
+
+ // Remove the configurations for migrated Passpoint configurations.
+ for (int networkId : legacyPasspointNetId) {
+ mConfiguredNetworks.remove(networkId);
+ }
+
+ // Setup store data for write.
+ mNetworkListStoreData.setSharedConfigurations(sharedConfigurations);
+ mNetworkListStoreData.setUserConfigurations(userConfigurations);
+ mDeletedEphemeralSsidsStoreData.setSsidList(mDeletedEphemeralSSIDs);
+
+ try {
+ mWifiConfigStore.write(forceWrite);
+ } catch (IOException e) {
+ Log.wtf(TAG, "Writing to store failed. Saved networks maybe lost!", e);
+ return false;
+ } catch (XmlPullParserException e) {
+ Log.wtf(TAG, "XML serialization for store failed. Saved networks maybe lost!", e);
+ return false;
+ }
+ 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 and other internal state of WifiConfigManager.
+ */
+ 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 ----");
+ pw.println("WifiConfigManager - Configured networks Begin ----");
+ for (WifiConfiguration network : getInternalConfiguredNetworks()) {
+ pw.println(network);
+ }
+ pw.println("WifiConfigManager - Configured networks End ----");
+ pw.println("WifiConfigManager - Next network ID to be allocated " + mNextNetworkId);
+ pw.println("WifiConfigManager - Last selected network ID " + mLastSelectedNetworkId);
+ }
+
+ /**
+ * Returns true if the given uid has permission to add, update or remove proxy settings
+ */
+ private boolean canModifyProxySettings(int uid) {
+ final DevicePolicyManagerInternal dpmi =
+ mWifiPermissionsWrapper.getDevicePolicyManagerInternal();
+ final boolean isUidProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ final boolean isUidDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(uid,
+ DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ final boolean hasConfigOverridePermission =
+ mWifiPermissionsUtil.checkConfigOverridePermission(uid);
+ // If |uid| corresponds to the device owner, allow all modifications.
+ if (isUidDeviceOwner || isUidProfileOwner || hasConfigOverridePermission) {
+ return true;
+ }
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "UID: " + uid + " cannot modify WifiConfiguration proxy settings."
+ + " ConfigOverride=" + hasConfigOverridePermission
+ + " DeviceOwner=" + isUidDeviceOwner
+ + " ProfileOwner=" + isUidProfileOwner);
+ }
+ return false;
+ }
+
+ /**
+ * Set the saved network update event listener
+ */
+ public void setOnSavedNetworkUpdateListener(OnSavedNetworkUpdateListener listener) {
+ mListener = listener;
}
}
diff --git a/service/java/com/android/server/wifi/WifiConfigStore.java b/service/java/com/android/server/wifi/WifiConfigStore.java
index e4aef24..659192b 100644
--- a/service/java/com/android/server/wifi/WifiConfigStore.java
+++ b/service/java/com/android/server/wifi/WifiConfigStore.java
@@ -16,1359 +16,573 @@
package com.android.server.wifi;
+import android.app.AlarmManager;
import android.content.Context;
-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.os.Environment;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
-import android.util.SparseArray;
+import android.util.Xml;
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.util.TelephonyUtil;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.AtomicFile;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.util.XmlUtil;
-import org.json.JSONException;
-import org.json.JSONObject;
+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.File;
import java.io.FileNotFoundException;
-import java.io.FileReader;
+import java.io.FileOutputStream;
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!!!
+ * store. Uses keystore for certificate/key management operations.
+ * NOTE: This class should only be used from WifiConfigManager and is not thread-safe!
*/
public class WifiConfigStore {
-
- public static final String TAG = "WifiConfigStore";
- // 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
- // serialized JSON dictionary.
- public static final String ID_STRING_VAR_NAME = "id_str";
- public static final String ID_STRING_KEY_FQDN = "fqdn";
- public static final String ID_STRING_KEY_CREATOR_UID = "creatorUid";
- 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 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;
-
- WifiConfigStore(Context context, WifiNative wifiNative, KeyStore keyStore, LocalLog localLog,
- boolean showNetworks, boolean verboseDebug) {
- 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;
- }
-
- private static String removeDoubleQuotes(String string) {
- int length = string.length();
- if ((length > 1) && (string.charAt(0) == '"')
- && (string.charAt(length - 1) == '"')) {
- return string.substring(1, length - 1);
- }
- return string;
- }
+ private static final String XML_TAG_DOCUMENT_HEADER = "WifiConfigStoreData";
+ private static final String XML_TAG_VERSION = "Version";
+ /**
+ * 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;
/**
- * Generate a string to be used as a key value by wpa_supplicant from
- * 'set', within the set of strings from 'strings' for the variable concatenated.
- * 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 strings the set of string literals to concatenate strinfs from.
- * @return A wpa_supplicant formatted value.
+ * Alarm tag to use for starting alarms for buffering file writes.
*/
- private static String makeString(BitSet set, String[] strings) {
- return makeStringWithException(set, strings, null);
- }
-
+ @VisibleForTesting
+ public static final String BUFFERED_WRITE_ALARM_TAG = "WriteBufferAlarm";
/**
- * 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 exception literal string to be excluded from the _ to - transformation.
- * @return A wpa_supplicant formatted value.
+ * Log tag.
*/
- private static String makeStringWithException(BitSet set, String[] strings, String exception) {
- StringBuilder result = new StringBuilder();
-
- /* Make sure all set bits are in [0, strings.length) to avoid
- * going out of bounds on strings. (Shouldn't happen, but...) */
- 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)) {
- String currentName = strings[bit];
- if (exception != null && currentName.equals(exception)) {
- valueSet.add(currentName);
- } else {
- // Most wpa_supplicant strings use a dash whereas (for some bizarre
- // reason) the strings are defined with underscore in the code...
- valueSet.add(currentName.replace('_', '-'));
- }
- }
- return TextUtils.join(" ", valueSet);
- }
-
- /*
- * Convert string to Hexadecimal before passing to wifi native layer
- * In native function "doCommand()" have trouble in converting Unicode character string to UTF8
- * conversion to hex is required because SSIDs can have space characters in them;
- * and that can confuses the supplicant because it uses space charaters as delimiters
- */
- private static String encodeSSID(String str) {
- 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;
-
- string = string.replace('-', '_');
-
- for (int i = 0; i < size; i++) {
- if (string.equals(strings[i])) {
- return i;
- }
- }
- loge("Failed to look-up a string: " + string);
- return -1;
- }
-
- private void readNetworkBitsetVariable(int netId, BitSet variable, String varName,
- String[] strings) {
- String value = mWifiNative.getNetworkVariable(netId, varName);
- if (!TextUtils.isEmpty(value)) {
- variable.clear();
- String[] vals = value.split(" ");
- for (String val : vals) {
- int index = lookupString(val, strings);
- if (0 <= index) {
- variable.set(index);
- }
- }
- }
- }
-
+ private static final String TAG = "WifiConfigStore";
/**
- * Read the variables from the supplicant daemon that are needed to
- * fill in the WifiConfiguration object.
- *
- * @param config the {@link WifiConfiguration} object to be filled in.
+ * Config store file name for both shared & user specific stores.
*/
- public void readNetworkVariables(WifiConfiguration config) {
- if (config == null) {
- return;
- }
- if (VDBG) localLog("readNetworkVariables: " + config.networkId);
- int netId = config.networkId;
- if (netId < 0) {
- return;
- }
- /*
- * TODO: maybe should have a native method that takes an array of
- * variable names and returns an array of values. But we'd still
- * be doing a round trip to the supplicant daemon for each variable.
- */
- String value;
-
- value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.ssidVarName);
- if (!TextUtils.isEmpty(value)) {
- if (value.charAt(0) != '"') {
- config.SSID = "\"" + WifiSsid.createFromHex(value).toString() + "\"";
- //TODO: convert a hex string that is not UTF-8 decodable to a P-formatted
- //supplicant string
- } else {
- config.SSID = value;
- }
- } else {
- config.SSID = null;
- }
-
- value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.bssidVarName);
- if (!TextUtils.isEmpty(value)) {
- config.getNetworkSelectionStatus().setNetworkSelectionBSSID(value);
- } else {
- config.getNetworkSelectionStatus().setNetworkSelectionBSSID(null);
- }
-
- value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.priorityVarName);
- config.priority = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.priority = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName);
- config.hiddenSSID = false;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.hiddenSSID = Integer.parseInt(value) != 0;
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pmfVarName);
- config.requirePMF = false;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.requirePMF = Integer.parseInt(value) == STORED_VALUE_FOR_REQUIRE_PMF;
- } catch (NumberFormatException ignore) {
- }
- }
-
- value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName);
- config.wepTxKeyIndex = -1;
- if (!TextUtils.isEmpty(value)) {
- try {
- config.wepTxKeyIndex = Integer.parseInt(value);
- } catch (NumberFormatException ignore) {
- }
- }
-
- for (int i = 0; i < 4; i++) {
- value = mWifiNative.getNetworkVariable(netId,
- WifiConfiguration.wepKeyVarNames[i]);
- if (!TextUtils.isEmpty(value)) {
- config.wepKeys[i] = value;
- } else {
- config.wepKeys[i] = null;
- }
- }
-
- value = mWifiNative.getNetworkVariable(netId, WifiConfiguration.pskVarName);
- if (!TextUtils.isEmpty(value)) {
- config.preSharedKey = value;
- } else {
- config.preSharedKey = null;
- }
-
- readNetworkBitsetVariable(config.networkId, config.allowedProtocols,
- WifiConfiguration.Protocol.varName, WifiConfiguration.Protocol.strings);
-
- readNetworkBitsetVariable(config.networkId, config.allowedKeyManagement,
- WifiConfiguration.KeyMgmt.varName, WifiConfiguration.KeyMgmt.strings);
- // The FT flags should not be exposed to external apps.
- config.allowedKeyManagement = removeFastTransitionFlags(config.allowedKeyManagement);
-
- readNetworkBitsetVariable(config.networkId, config.allowedAuthAlgorithms,
- WifiConfiguration.AuthAlgorithm.varName, WifiConfiguration.AuthAlgorithm.strings);
-
- readNetworkBitsetVariable(config.networkId, config.allowedPairwiseCiphers,
- WifiConfiguration.PairwiseCipher.varName, WifiConfiguration.PairwiseCipher.strings);
-
- readNetworkBitsetVariable(config.networkId, config.allowedGroupCiphers,
- WifiConfiguration.GroupCipher.varName, WifiConfiguration.GroupCipher.strings);
-
- if (config.enterpriseConfig == null) {
- config.enterpriseConfig = new WifiEnterpriseConfig();
- }
- config.enterpriseConfig.loadFromSupplicant(new SupplicantLoader(netId));
- }
-
+ private static final String STORE_FILE_NAME = "WifiConfigStore.xml";
/**
- * Load all the configured networks from wpa_supplicant.
- *
- * @param configs Map of configuration key to configuration objects corresponding to all
- * the networks.
- * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf
- * @return Max priority of all the configs.
+ * Directory to store the config store files in.
*/
- public int loadNetworks(Map<String, WifiConfiguration> configs,
- SparseArray<Map<String, String>> networkExtras) {
- int lastPriority = 0;
- int last_id = -1;
- boolean done = false;
- while (!done) {
- String listStr = mWifiNative.listNetworks(last_id);
- if (listStr == null) {
- return lastPriority;
- }
- String[] lines = listStr.split("\n");
- if (mShowNetworks) {
- localLog("loadNetworks: ");
- for (String net : lines) {
- localLog(net);
- }
- }
- // Skip the first line, which is a header
- for (int i = 1; i < lines.length; i++) {
- String[] result = lines[i].split("\t");
- // network-id | ssid | bssid | flags
- WifiConfiguration config = new WifiConfiguration();
- try {
- config.networkId = Integer.parseInt(result[0]);
- last_id = config.networkId;
- } catch (NumberFormatException e) {
- loge("Failed to read network-id '" + result[0] + "'");
- continue;
- }
- // Ignore the supplicant status, start all networks disabled.
- config.status = WifiConfiguration.Status.DISABLED;
- readNetworkVariables(config);
- // Parse the serialized JSON dictionary in ID_STRING_VAR_NAME once and cache the
- // result for efficiency.
- Map<String, String> extras = mWifiNative.getNetworkExtra(config.networkId,
- ID_STRING_VAR_NAME);
- if (extras == null) {
- extras = new HashMap<String, String>();
- // If ID_STRING_VAR_NAME did not contain a dictionary, assume that it contains
- // just a quoted FQDN. This is the legacy format that was used in Marshmallow.
- final String fqdn = Utils.unquote(mWifiNative.getNetworkVariable(
- config.networkId, ID_STRING_VAR_NAME));
- if (fqdn != null) {
- extras.put(ID_STRING_KEY_FQDN, fqdn);
- config.FQDN = fqdn;
- // Mark the configuration as a Hotspot 2.0 network.
- config.providerFriendlyName = "";
- }
- }
- networkExtras.put(config.networkId, extras);
-
- if (config.priority > lastPriority) {
- lastPriority = config.priority;
- }
- config.setIpAssignment(IpAssignment.DHCP);
- config.setProxySettings(ProxySettings.NONE);
- // The configKey is explicitly stored in wpa_supplicant.conf, because config does
- // not contain sufficient information to compute it at this point.
- String configKey = extras.get(ID_STRING_KEY_CONFIG_KEY);
- if (configKey == null) {
- // Handle the legacy case where the configKey is not stored in
- // wpa_supplicant.conf but can be computed straight away.
- // Force an update of this legacy network configuration by writing
- // the configKey for this network into wpa_supplicant.conf.
- configKey = config.configKey();
- saveNetworkMetadata(config);
- }
- final WifiConfiguration duplicateConfig = configs.put(configKey, config);
- if (duplicateConfig != null) {
- // The network is already known. Overwrite the duplicate entry.
- if (mShowNetworks) {
- localLog("Replacing duplicate network " + duplicateConfig.networkId
- + " with " + config.networkId + ".");
- }
- // This can happen after the user manually connected to an AP and tried to use
- // WPS to connect the AP later. In this case, the supplicant will create a new
- // network for the AP although there is an existing network already.
- mWifiNative.removeNetwork(duplicateConfig.networkId);
- }
- }
- done = (lines.length == 1);
- }
- return lastPriority;
- }
-
+ private static final String STORE_DIRECTORY_NAME = "wifi";
/**
- * 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.
+ * Time interval for buffering file writes for non-forced writes
*/
- 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;
- }
- }
-
+ private static final int BUFFERED_WRITE_ALARM_INTERVAL_MS = 10 * 1000;
/**
- * Remove enterprise keys from the network config.
- *
- * @param config Config corresponding to the network.
+ * Handler instance to post alarm timeouts to
*/
- 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);
- }
- }
- }
- }
-
+ private final Handler mEventHandler;
/**
- * 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.
+ * Alarm manager instance to start buffer timeout alarms.
*/
- public boolean saveNetworkMetadata(WifiConfiguration config) {
- final Map<String, String> metadata = new HashMap<String, String>();
- if (config.isPasspoint()) {
- metadata.put(ID_STRING_KEY_FQDN, config.FQDN);
- }
- metadata.put(ID_STRING_KEY_CONFIG_KEY, config.configKey());
- metadata.put(ID_STRING_KEY_CREATOR_UID, Integer.toString(config.creatorUid));
- if (!mWifiNative.setNetworkExtra(config.networkId, ID_STRING_VAR_NAME, metadata)) {
- loge("failed to set id_str: " + metadata.toString());
- return false;
- }
- return true;
- }
-
- private BitSet addFastTransitionFlags(BitSet keyManagementFlags) {
- BitSet modifiedFlags = keyManagementFlags;
- if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
- modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_PSK);
- }
- if (keyManagementFlags.get(WifiConfiguration.KeyMgmt.WPA_EAP)) {
- modifiedFlags.set(WifiConfiguration.KeyMgmt.FT_EAP);
- }
- return modifiedFlags;
- }
-
- private BitSet removeFastTransitionFlags(BitSet keyManagementFlags) {
- BitSet modifiedFlags = keyManagementFlags;
- modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_PSK);
- modifiedFlags.clear(WifiConfiguration.KeyMgmt.FT_EAP);
- return modifiedFlags;
- }
-
+ private final AlarmManager mAlarmManager;
/**
- * Save an entire network configuration to wpa_supplicant.
- *
- * @param config Config corresponding to the network.
- * @param netId Net Id of the network.
- * @param addFastTransitionFlags Add the BSS fast transition(80211r) flags to the network.
- * @return true if successful, false otherwise.
+ * Clock instance to retrieve timestamps for alarms.
*/
- private boolean saveNetwork(WifiConfiguration config, int netId,
- boolean addFastTransitionFlags) {
- if (config == null) {
- return false;
- }
- if (VDBG) localLog("saveNetwork: " + netId);
- if (config.SSID != null && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.ssidVarName,
- encodeSSID(config.SSID))) {
- loge("failed to set SSID: " + config.SSID);
- return false;
- }
- if (!saveNetworkMetadata(config)) {
- return false;
- }
- //set selected BSSID to supplicant
- if (config.getNetworkSelectionStatus().getNetworkSelectionBSSID() != null) {
- String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
- if (!mWifiNative.setNetworkVariable(netId, WifiConfiguration.bssidVarName, bssid)) {
- loge("failed to set BSSID: " + bssid);
- return false;
- }
- }
- BitSet allowedKeyManagement = config.allowedKeyManagement;
- if (addFastTransitionFlags) {
- allowedKeyManagement = addFastTransitionFlags(config.allowedKeyManagement);
- }
- String allowedKeyManagementString =
- makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings);
- if (config.allowedKeyManagement.cardinality() != 0 && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.KeyMgmt.varName,
- allowedKeyManagementString)) {
- loge("failed to set key_mgmt: " + allowedKeyManagementString);
- return false;
- }
- String allowedProtocolsString =
- makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings);
- if (config.allowedProtocols.cardinality() != 0 && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.Protocol.varName,
- allowedProtocolsString)) {
- loge("failed to set proto: " + allowedProtocolsString);
- return false;
- }
- String allowedAuthAlgorithmsString =
- makeString(config.allowedAuthAlgorithms,
- WifiConfiguration.AuthAlgorithm.strings);
- if (config.allowedAuthAlgorithms.cardinality() != 0 && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.AuthAlgorithm.varName,
- allowedAuthAlgorithmsString)) {
- loge("failed to set auth_alg: " + allowedAuthAlgorithmsString);
- return false;
- }
- String allowedPairwiseCiphersString = makeString(config.allowedPairwiseCiphers,
- WifiConfiguration.PairwiseCipher.strings);
- if (config.allowedPairwiseCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.PairwiseCipher.varName,
- allowedPairwiseCiphersString)) {
- loge("failed to set pairwise: " + allowedPairwiseCiphersString);
- return false;
- }
- // Make sure that the string "GTK_NOT_USED" is /not/ transformed - wpa_supplicant
- // uses this literal value and not the 'dashed' version.
- String allowedGroupCiphersString =
- makeStringWithException(config.allowedGroupCiphers,
- WifiConfiguration.GroupCipher.strings,
- WifiConfiguration.GroupCipher
- .strings[WifiConfiguration.GroupCipher.GTK_NOT_USED]);
- if (config.allowedGroupCiphers.cardinality() != 0 && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.GroupCipher.varName,
- allowedGroupCiphersString)) {
- loge("failed to set group: " + allowedGroupCiphersString);
- return false;
- }
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.preSharedKey != null && !config.preSharedKey.equals("*")
- && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.pskVarName,
- config.preSharedKey)) {
- loge("failed to set psk");
- return false;
- }
- boolean hasSetKey = false;
- if (config.wepKeys != null) {
- for (int i = 0; i < config.wepKeys.length; i++) {
- // Prevent client screw-up by passing in a WifiConfiguration we gave it
- // by preventing "*" as a key.
- if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) {
- if (!mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.wepKeyVarNames[i],
- config.wepKeys[i])) {
- loge("failed to set wep_key" + i + ": " + config.wepKeys[i]);
- return false;
- }
- hasSetKey = true;
- }
- }
- }
- if (hasSetKey) {
- if (!mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.wepTxKeyIdxVarName,
- Integer.toString(config.wepTxKeyIndex))) {
- loge("failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
- return false;
- }
- }
- if (!mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.priorityVarName,
- Integer.toString(config.priority))) {
- loge(config.SSID + ": failed to set priority: " + config.priority);
- return false;
- }
- if (config.hiddenSSID && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.hiddenSSIDVarName,
- Integer.toString(config.hiddenSSID ? 1 : 0))) {
- loge(config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID);
- return false;
- }
- if (config.requirePMF && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.pmfVarName,
- Integer.toString(STORED_VALUE_FOR_REQUIRE_PMF))) {
- loge(config.SSID + ": failed to set requirePMF: " + config.requirePMF);
- return false;
- }
- if (config.updateIdentifier != null && !mWifiNative.setNetworkVariable(
- netId,
- WifiConfiguration.updateIdentiferVarName,
- config.updateIdentifier)) {
- loge(config.SSID + ": failed to set updateIdentifier: " + config.updateIdentifier);
- return false;
- }
- return true;
- }
-
+ private final Clock mClock;
/**
- * 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.
+ * Shared config store file instance.
*/
- 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);
- return false;
- }
- return true;
- }
-
+ private StoreFile mSharedStore;
/**
- * 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.
- * @param addFastTransitionFlags Add the BSS fast transition(80211r) flags to the network.
- * @return true if successful, false otherwise.
+ * User specific store file instance.
*/
- public boolean addOrUpdateNetwork(WifiConfiguration config, WifiConfiguration existingConfig,
- boolean addFastTransitionFlags) {
- if (config == null) {
- return false;
- }
- if (VDBG) localLog("addOrUpdateNetwork: " + config.networkId);
- int netId = config.networkId;
- boolean newNetwork = false;
- /*
- * If the supplied networkId is INVALID_NETWORK_ID, we create a new empty
- * network configuration. Otherwise, the networkId should
- * refer to an existing configuration.
- */
- if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
- newNetwork = true;
- netId = mWifiNative.addNetwork();
- if (netId < 0) {
- loge("Failed to add a network!");
- return false;
- } else {
- logi("addOrUpdateNetwork created netId=" + netId);
- }
- // Save the new network ID to the config
- config.networkId = netId;
- }
- if (!saveNetwork(config, netId, addFastTransitionFlags)) {
- if (newNetwork) {
- mWifiNative.removeNetwork(netId);
- loge("Failed to set a network variable, removed network: " + netId);
- }
- 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;
- }
-
+ private StoreFile mUserStore;
/**
- * Remove the specified network and save config
- *
- * @param config Config corresponding to the network.
- * @return {@code true} if it succeeds, {@code false} otherwise
+ * Verbose logging flag.
*/
- public boolean removeNetwork(WifiConfiguration config) {
- if (config == null) {
- return false;
- }
- if (VDBG) 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;
- }
-
+ private boolean mVerboseLoggingEnabled = false;
/**
- * Select a network in wpa_supplicant.
- *
- * @param config Config corresponding to the network.
- * @return true if successful, false otherwise.
+ * Flag to indicate if there is a buffered write pending.
*/
- public boolean selectNetwork(WifiConfiguration config, Collection<WifiConfiguration> configs) {
- if (config == null) {
- return false;
- }
- if (VDBG) 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;
- }
-
+ private boolean mBufferedWritePending = false;
/**
- * Disable a network in wpa_supplicant.
- *
- * @param config Config corresponding to the network.
- * @return true if successful, false otherwise.
+ * Alarm listener for flushing out any buffered writes.
*/
- boolean disableNetwork(WifiConfiguration config) {
- if (config == null) {
- return false;
- }
- if (VDBG) 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;
- }
-
- /**
- * Set priority for a network in wpa_supplicant.
- *
- * @param config Config corresponding to the network.
- * @return true if successful, false otherwise.
- */
- public boolean setNetworkPriority(WifiConfiguration config, int priority) {
- if (config == null) {
- return false;
- }
- if (VDBG) 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);
- return false;
- }
- config.priority = priority;
- return true;
- }
-
- /**
- * Set SSID for a network in wpa_supplicant.
- *
- * @param config Config corresponding to the network.
- * @return true if successful, false otherwise.
- */
- public boolean setNetworkSSID(WifiConfiguration config, String ssid) {
- if (config == null) {
- return false;
- }
- if (VDBG) 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);
- return false;
- }
- config.SSID = ssid;
- return true;
- }
-
- /**
- * Set BSSID for a network in wpa_supplicant from network selection.
- *
- * @param config Config corresponding to the network.
- * @param bssid BSSID to be set.
- * @return true if successful, false otherwise.
- */
- public boolean setNetworkBSSID(WifiConfiguration config, String bssid) {
- // Sanity check the config is valid
- if (config == null
- || (config.networkId == WifiConfiguration.INVALID_NETWORK_ID
- && config.SSID == null)) {
- return false;
- }
- if (VDBG) localLog("setNetworkBSSID: " + config.networkId);
- if (!mWifiNative.setNetworkVariable(config.networkId, WifiConfiguration.bssidVarName,
- bssid)) {
- loge("Set BSSID of network in wpa_supplicant failed on " + config.networkId);
- return false;
- }
- config.getNetworkSelectionStatus().setNetworkSelectionBSSID(bssid);
- return true;
- }
-
- /**
- * Enable/Disable HS20 parameter in wpa_supplicant.
- *
- * @param enable Enable/Disable the parameter.
- */
- public void enableHS20(boolean enable) {
- mWifiNative.setHs20(enable);
- }
-
- /**
- * Disables all the networks in the provided list in wpa_supplicant.
- *
- * @param configs Collection of configs which needs to be enabled.
- * @return true if successful, false otherwise.
- */
- public boolean disableAllNetworks(Collection<WifiConfiguration> configs) {
- if (VDBG) localLog("disableAllNetworks");
- boolean networkDisabled = false;
- for (WifiConfiguration enabled : configs) {
- if (disableNetwork(enabled)) {
- networkDisabled = true;
- }
- }
- saveConfig();
- return networkDisabled;
- }
-
- /**
- * Save the current configuration to wpa_supplicant.conf.
- */
- public boolean saveConfig() {
- return mWifiNative.saveConfig();
- }
-
- /**
- * Read network variables from wpa_supplicant.conf.
- *
- * @param key The parameter to be parsed.
- * @return Map of corresponding configKey to the value of the param requested.
- */
- public Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
- Map<String, String> result = new HashMap<>();
- BufferedReader reader = null;
- try {
- 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);
- } catch (IOException e) {
- if (VDBG) loge("Could not read " + SUPPLICANT_CONFIG_FILE + ", " + e);
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (IOException e) {
- if (VDBG) {
- loge("Could not close reader for " + SUPPLICANT_CONFIG_FILE + ", " + e);
- }
- }
- }
- return result;
- }
-
- /**
- * Read network variables from a given reader. This method is separate from
- * readNetworkVariablesFromSupplicantFile() for testing.
- *
- * @param reader The reader to read the network variables from.
- * @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);
- boolean found = false;
- String configKey = null;
- String value = null;
- for (String line = reader.readLine(); line != null; line = reader.readLine()) {
- if (line.matches("[ \\t]*network=\\{")) {
- found = true;
- configKey = null;
- value = null;
- } else if (line.matches("[ \\t]*\\}")) {
- found = false;
- configKey = null;
- value = null;
- }
- if (found) {
- String trimmedLine = line.trim();
- if (trimmedLine.startsWith(ID_STRING_VAR_NAME + "=")) {
+ private final AlarmManager.OnAlarmListener mBufferedWriteListener =
+ new AlarmManager.OnAlarmListener() {
+ public void onAlarm() {
try {
- // Trim the quotes wrapping the id_str value.
- final String encodedExtras = trimmedLine.substring(
- 8, trimmedLine.length() -1);
- final JSONObject json =
- new JSONObject(URLDecoder.decode(encodedExtras, "UTF-8"));
- if (json.has(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY)) {
- final Object configKeyFromJson =
- json.get(WifiConfigStore.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);
- }
+ writeBufferedData();
+ } catch (IOException e) {
+ Log.wtf(TAG, "Buffered write failed", e);
}
+
}
- if (trimmedLine.startsWith(key + "=")) {
- value = trimmedLine.substring(key.length() + 1);
- }
- if (configKey != null && value != null) {
- result.put(configKey, value);
- }
- }
- }
- return result;
+ };
+
+ /**
+ * List of data container.
+ */
+ private final Map<String, StoreData> mStoreDataList;
+
+ /**
+ * 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.
+ */
+ public WifiConfigStore(Context context, Looper looper, Clock clock,
+ StoreFile sharedStore) {
+
+ mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mEventHandler = new Handler(looper);
+ mClock = clock;
+ mStoreDataList = new HashMap<>();
+
+ // Initialize the store files.
+ mSharedStore = sharedStore;
+ // The user store is initialized to null, this will be set when the user unlocks and
+ // CE storage is accessible via |switchUserStoreAndRead|.
+ mUserStore = null;
+ }
+
+ public void setUserStore(StoreFile userStore) {
+ mUserStore = userStore;
}
/**
- * Resets all sim networks from the provided network list.
+ * Register a {@link StoreData} to store. A {@link StoreData} is responsible
+ * for a block of data in the store file, and provides serialization/deserialization functions
+ * for those data.
*
- * @param configs List of all the networks.
+ * @param storeData The store data to be registered to the config store
+ * @return true if succeeded
*/
- public void resetSimNetworks(Collection<WifiConfiguration> configs) {
- if (VDBG) localLog("resetSimNetworks");
- for (WifiConfiguration config : configs) {
- if (TelephonyUtil.isSimConfig(config)) {
- String currentIdentity = TelephonyUtil.getSimIdentity(mContext,
- config.enterpriseConfig.getEapMethod());
- String supplicantIdentity =
- mWifiNative.getNetworkVariable(config.networkId, "identity");
- if(supplicantIdentity != null) {
- supplicantIdentity = removeDoubleQuotes(supplicantIdentity);
- }
- if (currentIdentity == null || !currentIdentity.equals(supplicantIdentity)) {
- // Identity differs so update the identity
- mWifiNative.setNetworkVariable(config.networkId,
- WifiEnterpriseConfig.IDENTITY_KEY, WifiEnterpriseConfig.EMPTY_VALUE);
- // This configuration may have cached Pseudonym IDs; lets remove them
- mWifiNative.setNetworkVariable(config.networkId,
- WifiEnterpriseConfig.ANON_IDENTITY_KEY,
- WifiEnterpriseConfig.EMPTY_VALUE);
- }
- // Update the loaded config
- config.enterpriseConfig.setIdentity(currentIdentity);
- config.enterpriseConfig.setAnonymousIdentity("");
+ public boolean registerStoreData(StoreData storeData) {
+ if (storeData == null) {
+ Log.e(TAG, "Unable to register null store data");
+ return false;
+ }
+ mStoreDataList.put(storeData.getName(), storeData);
+ return true;
+ }
+
+ /**
+ * Helper method to create a store file instance for either the shared store or user store.
+ * Note: The method creates the store directory if not already present. This may be needed for
+ * user store files.
+ *
+ * @param storeBaseDir Base directory under which the store file is to be stored. The store file
+ * will be at <storeBaseDir>/wifi/WifiConfigStore.xml.
+ * @return new instance of the store file.
+ */
+ private static StoreFile createFile(File storeBaseDir) {
+ File storeDir = new File(storeBaseDir, STORE_DIRECTORY_NAME);
+ if (!storeDir.exists()) {
+ if (!storeDir.mkdir()) {
+ Log.w(TAG, "Could not create store directory " + storeDir);
}
}
+ return new StoreFile(new File(storeDir, STORE_FILE_NAME));
+ }
+
+ /**
+ * Create a new instance of the shared store file.
+ *
+ * @return new instance of the store file or null if the directory cannot be created.
+ */
+ public static StoreFile createSharedFile() {
+ return createFile(Environment.getDataMiscDirectory());
+ }
+
+ /**
+ * 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 or null if the directory cannot be created.
+ */
+ public static StoreFile createUserFile(int userId) {
+ return createFile(Environment.getDataMiscCeDirectory(userId));
+ }
+
+ /**
+ * Enable verbose logging.
+ */
+ public void enableVerboseLogging(boolean verbose) {
+ mVerboseLoggingEnabled = verbose;
+ }
+
+ /**
+ * API to check if any of the store files are present on the device. This can be used
+ * to detect if the device needs to perform data migration from legacy stores.
+ *
+ * @return true if any of the store file is present, false otherwise.
+ */
+ public boolean areStoresPresent() {
+ return (mSharedStore.exists() || (mUserStore != null && mUserStore.exists()));
+ }
+
+ /**
+ * API to write the data provided by registered 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.
+ */
+ public void write(boolean forceSync)
+ 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 = serializeData(true);
+ mSharedStore.storeRawDataToWrite(sharedDataBytes);
+ if (mUserStore != null) {
+ byte[] userDataBytes = serializeData(false);
+ 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();
+ }
}
/**
- * Clear BSSID blacklist in wpa_supplicant.
+ * Serialize share data or user data from all store data.
+ *
+ * @param shareData Flag indicating share data
+ * @return byte[] of serialized bytes
+ * @throws XmlPullParserException
+ * @throws IOException
*/
- public void clearBssidBlacklist() {
- if (VDBG) localLog("clearBlacklist");
- mBssidBlacklist.clear();
- mWifiNative.clearBlacklist();
- mWifiNative.setBssidBlacklist(null);
+ private byte[] serializeData(boolean shareData) throws XmlPullParserException, IOException {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+
+ XmlUtil.writeDocumentStart(out, XML_TAG_DOCUMENT_HEADER);
+ XmlUtil.writeNextValue(out, XML_TAG_VERSION, CURRENT_CONFIG_STORE_DATA_VERSION);
+
+ for (Map.Entry<String, StoreData> entry : mStoreDataList.entrySet()) {
+ String tag = entry.getKey();
+ StoreData storeData = entry.getValue();
+ // Ignore this store data if this is for share file and the store data doesn't support
+ // share store.
+ if (shareData && !storeData.supportShareData()) {
+ continue;
+ }
+ XmlUtil.writeNextSectionStart(out, tag);
+ storeData.serializeData(out, shareData);
+ XmlUtil.writeNextSectionEnd(out, tag);
+ }
+ XmlUtil.writeDocumentEnd(out, XML_TAG_DOCUMENT_HEADER);
+
+ return outputStream.toByteArray();
}
/**
- * Add a BSSID to the blacklist.
- *
- * @param bssid bssid to be added.
+ * Helper method to start a buffered write alarm if one doesn't already exist.
*/
- public void blackListBssid(String bssid) {
- if (bssid == null) {
+ 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();
+
+ long writeStartTime = mClock.getElapsedSinceBootMillis();
+ mSharedStore.writeBufferedRawData();
+ if (mUserStore != null) {
+ mUserStore.writeBufferedRawData();
+ }
+ long writeTime = mClock.getElapsedSinceBootMillis() - writeStartTime;
+
+ Log.d(TAG, "Writing to stores completed in " + writeTime + " ms.");
+ }
+
+ /**
+ * 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.
+ */
+ public void read() throws XmlPullParserException, IOException {
+ // Reset both share and user store data.
+ resetStoreData(true);
+ resetStoreData(false);
+
+ long readStartTime = mClock.getElapsedSinceBootMillis();
+ byte[] sharedDataBytes = mSharedStore.readRawData();
+ byte[] userDataBytes = null;
+ if (mUserStore != null) {
+ userDataBytes = mUserStore.readRawData();
+ }
+ long readTime = mClock.getElapsedSinceBootMillis() - readStartTime;
+ Log.d(TAG, "Reading from stores completed in " + readTime + " ms.");
+ deserializeData(sharedDataBytes, true);
+ deserializeData(userDataBytes, false);
+ }
+
+ /**
+ * Handles a user switch. This method changes the user specific store file and reads from the
+ * new user's store file.
+ *
+ * @param userStore StoreFile instance pointing to the user specific store file. This should
+ * be retrieved using {@link #createUserFile(int)} method.
+ */
+ public void switchUserStoreAndRead(StoreFile userStore)
+ throws XmlPullParserException, IOException {
+ // Reset user store data.
+ resetStoreData(false);
+
+ // Stop any pending buffered writes, if any.
+ stopBufferedWriteAlarm();
+ mUserStore = userStore;
+
+ // Now read from the user store file.
+ long readStartTime = mClock.getElapsedSinceBootMillis();
+ byte[] userDataBytes = mUserStore.readRawData();
+ long readTime = mClock.getElapsedSinceBootMillis() - readStartTime;
+ Log.d(TAG, "Reading from user store completed in " + readTime + " ms.");
+ deserializeData(userDataBytes, false);
+ }
+
+ /**
+ * Reset share data or user data in all store data.
+ *
+ * @param shareData Flag indicating share data
+ */
+ private void resetStoreData(boolean shareData) {
+ for (Map.Entry<String, StoreData> entry : mStoreDataList.entrySet()) {
+ entry.getValue().resetData(shareData);
+ }
+ }
+
+ /**
+ * Deserialize share data or user data into store data.
+ *
+ * @param dataBytes The data to parse
+ * @param shareData The flag indicating share data
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private void deserializeData(byte[] dataBytes, boolean shareData)
+ throws XmlPullParserException, IOException {
+ if (dataBytes == null) {
return;
}
- if (VDBG) localLog("blackListBssid: " + bssid);
- mBssidBlacklist.add(bssid);
- // Blacklist at wpa_supplicant
- mWifiNative.addToBlacklist(bssid);
- // Blacklist at firmware
- String[] list = mBssidBlacklist.toArray(new String[mBssidBlacklist.size()]);
- mWifiNative.setBssidBlacklist(list);
+ final XmlPullParser in = Xml.newPullParser();
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(dataBytes);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+
+ // Start parsing the XML stream.
+ int rootTagDepth = in.getDepth() + 1;
+ parseDocumentStartAndVersionFromXml(in);
+
+ String[] headerName = new String[1];
+ while (XmlUtil.gotoNextSectionOrEnd(in, headerName, rootTagDepth)) {
+ StoreData storeData = mStoreDataList.get(headerName[0]);
+ if (storeData == null) {
+ throw new XmlPullParserException("Unknown store data: " + headerName[0]);
+ }
+ storeData.deserializeData(in, rootTagDepth + 1, shareData);
+ }
}
/**
- * Checks if the provided bssid is blacklisted or not.
+ * Parse the document start and version from the XML stream.
+ * This is used for both the shared and user config store data.
*
- * @param bssid bssid to be checked.
- * @return true if present, false otherwise.
+ * @param in XmlPullParser instance pointing to the XML stream.
+ * @return version number retrieved from the Xml stream.
*/
- 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 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);
}
- }
-
- private void markAllNetworksDisabled(Collection<WifiConfiguration> configs) {
- markAllNetworksDisabledExcept(WifiConfiguration.INVALID_NETWORK_ID, configs);
+ return version;
}
/**
- * Start WPS pin method configuration with pin obtained
- * from the access point
- *
- * @param config WPS configuration
- * @return Wps result containing status and pin
+ * 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 WpsResult startWpsWithPinFromAccessPoint(WpsInfo config,
- Collection<WifiConfiguration> configs) {
- 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");
- result.status = WpsResult.Status.FAILURE;
- }
- return result;
- }
+ 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;
- /**
- * Start WPS pin method configuration with obtained
- * from the device
- *
- * @return WpsResult indicating status and pin
- */
- public WpsResult startWpsWithPinFromDevice(WpsInfo config,
- Collection<WifiConfiguration> configs) {
- 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");
- result.status = WpsResult.Status.FAILURE;
- }
- return result;
- }
-
- /**
- * Start WPS push button configuration
- *
- * @param config WPS configuration
- * @return WpsResult indicating status and pin
- */
- public WpsResult startWpsPbc(WpsInfo config,
- Collection<WifiConfiguration> configs) {
- 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");
- result.status = WpsResult.Status.FAILURE;
- }
- return result;
- }
-
- protected void logd(String s) {
- Log.d(TAG, s);
- }
-
- protected void logi(String s) {
- Log.i(TAG, s);
- }
-
- protected void loge(String s) {
- loge(s, false);
- }
-
- protected void loge(String s, boolean stack) {
- if (stack) {
- Log.e(TAG, s + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
- + " - " + Thread.currentThread().getStackTrace()[3].getMethodName()
- + " - " + Thread.currentThread().getStackTrace()[4].getMethodName()
- + " - " + Thread.currentThread().getStackTrace()[5].getMethodName());
- } else {
- Log.e(TAG, s);
- }
- }
-
- protected void log(String s) {
- Log.d(TAG, s);
- }
-
- private void localLog(String s) {
- if (mLocalLog != null) {
- mLocalLog.log(TAG + ": " + s);
- }
- }
-
- private void localLogAndLogcat(String s) {
- localLog(s);
- Log.d(TAG, s);
- }
-
- private class SupplicantSaver implements WifiEnterpriseConfig.SupplicantSaver {
- private final int mNetId;
- private final String mSetterSSID;
-
- SupplicantSaver(int netId, String setterSSID) {
- mNetId = netId;
- mSetterSSID = setterSSID;
+ public StoreFile(File file) {
+ mAtomicFile = new AtomicFile(file);
+ mFileName = mAtomicFile.getBaseFile().getAbsolutePath();
}
- @Override
- public boolean saveValue(String key, String value) {
- if (key.equals(WifiEnterpriseConfig.PASSWORD_KEY)
- && value != null && value.equals("*")) {
- // No need to try to set an obfuscated password, which will fail
- return true;
- }
- if (key.equals(WifiEnterpriseConfig.REALM_KEY)
- || key.equals(WifiEnterpriseConfig.PLMN_KEY)) {
- // No need to save realm or PLMN in supplicant
- return true;
- }
- // TODO: We need a way to clear values in wpa_supplicant as opposed to
- // mapping unset values to empty strings.
- if (value == null) {
- value = "\"\"";
- }
- if (!mWifiNative.setNetworkVariable(mNetId, key, value)) {
- loge(mSetterSSID + ": failed to set " + key + ": " + value);
- return false;
- }
- return true;
- }
- }
-
- private class SupplicantLoader implements WifiEnterpriseConfig.SupplicantLoader {
- private final int mNetId;
-
- SupplicantLoader(int netId) {
- mNetId = netId;
+ /**
+ * Returns whether the store file already exists on disk or not.
+ *
+ * @return true if it exists, false otherwise.
+ */
+ public boolean exists() {
+ return mAtomicFile.exists();
}
- @Override
- public String loadValue(String key) {
- String value = mWifiNative.getNetworkVariable(mNetId, key);
- if (!TextUtils.isEmpty(value)) {
- if (!enterpriseConfigKeyShouldBeQuoted(key)) {
- value = removeDoubleQuotes(value);
- }
- return value;
- } else {
+ /**
+ * 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;
}
}
/**
- * Returns true if a particular config key needs to be quoted when passed to the supplicant.
+ * 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.
*/
- private boolean enterpriseConfigKeyShouldBeQuoted(String key) {
- switch (key) {
- case WifiEnterpriseConfig.EAP_KEY:
- case WifiEnterpriseConfig.ENGINE_KEY:
- return false;
- default:
- return true;
+ 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;
}
}
- // TODO(rpius): Remove this.
- private class WpaConfigFileObserver extends FileObserver {
+ /**
+ * Interface to be implemented by a module that contained data in the config store file.
+ *
+ * The module will be responsible for serializing/deserializing their own data.
+ */
+ public interface StoreData {
+ /**
+ * Serialize a XML data block to the output stream. The |shared| flag indicates if the
+ * output stream is backed by a share store or an user store.
+ *
+ * @param out The output stream to serialize the data to
+ * @param shared Flag indicating if the output stream is backed by a share store or an
+ * user store
+ */
+ void serializeData(XmlSerializer out, boolean shared)
+ throws XmlPullParserException, IOException;
- WpaConfigFileObserver() {
- super(SUPPLICANT_CONFIG_FILE, CLOSE_WRITE);
- }
+ /**
+ * Deserialize a XML data block from the input stream. The |shared| flag indicates if the
+ * input stream is backed by a share store or an user store. When |shared| is set to true,
+ * the shared configuration data will be overwritten by the parsed data. Otherwise,
+ * the user configuration will be overwritten by the parsed data.
+ *
+ * @param in The input stream to read the data from
+ * @param outerTagDepth The depth of the outer tag in the XML document
+ * @Param shared Flag indicating if the input stream is backed by a share store or an
+ * user store
+ */
+ void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
+ throws XmlPullParserException, IOException;
- @Override
- 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());
- }
- }
+ /**
+ * Reset configuration data. The |shared| flag indicates which configuration data to
+ * reset. When |shared| is set to true, the shared configuration data will be reset.
+ * Otherwise, the user configuration data will be reset.
+ */
+ void resetData(boolean shared);
+
+ /**
+ * Return the name of this store data. The data will be enclosed under this tag in
+ * the XML block.
+ *
+ * @return The name of the store data
+ */
+ String getName();
+
+ /**
+ * Flag indicating if shared configuration data is supported.
+ *
+ * @return true if shared configuration data is supported
+ */
+ boolean supportShareData();
}
}
diff --git a/service/java/com/android/server/wifi/WifiConfigStoreLegacy.java b/service/java/com/android/server/wifi/WifiConfigStoreLegacy.java
new file mode 100644
index 0000000..8677755
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiConfigStoreLegacy.java
@@ -0,0 +1,358 @@
+/*
+ * 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.os.Environment;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.server.net.IpConfigStore;
+import com.android.server.wifi.hotspot2.LegacyPasspointConfig;
+import com.android.server.wifi.hotspot2.LegacyPasspointConfigParser;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+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 load network configurations from legacy store
+ * mechanism (Pre O release).
+ * This class loads network configurations from:
+ * 1. /data/misc/wifi/networkHistory.txt
+ * 2. /data/misc/wifi/wpa_supplicant.conf
+ * 3. /data/misc/wifi/ipconfig.txt
+ * 4. /data/misc/wifi/PerProviderSubscription.conf
+ *
+ * The order of invocation of the public methods during migration is the following:
+ * 1. Check if legacy stores are present using {@link #areStoresPresent()}.
+ * 2. Load all the store data using {@link #read()}
+ * 3. Write the store data to the new store.
+ * 4. Remove all the legacy stores using {@link #removeStores()}
+ *
+ * NOTE: This class should only be used from WifiConfigManager and is not thread-safe!
+ *
+ * TODO(b/31065385): Passpoint config store data migration & deletion.
+ */
+public class WifiConfigStoreLegacy {
+ /**
+ * Log tag.
+ */
+ private static final String TAG = "WifiConfigStoreLegacy";
+ /**
+ * NetworkHistory config store file path.
+ */
+ private static final File NETWORK_HISTORY_FILE =
+ new File(WifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE);
+ /**
+ * Passpoint config store file path.
+ */
+ private static final File PPS_FILE =
+ new File(Environment.getDataMiscDirectory(), "wifi/PerProviderSubscription.conf");
+ /**
+ * IpConfig config store file path.
+ */
+ private static final File IP_CONFIG_FILE =
+ new File(Environment.getDataMiscDirectory(), "wifi/ipconfig.txt");
+ /**
+ * List of external dependencies for WifiConfigManager.
+ */
+ private final WifiNetworkHistory mWifiNetworkHistory;
+ private final WifiNative mWifiNative;
+ private final IpConfigStore mIpconfigStore;
+
+ private final LegacyPasspointConfigParser mPasspointConfigParser;
+
+ WifiConfigStoreLegacy(WifiNetworkHistory wifiNetworkHistory,
+ WifiNative wifiNative, IpConfigStore ipConfigStore,
+ LegacyPasspointConfigParser passpointConfigParser) {
+ mWifiNetworkHistory = wifiNetworkHistory;
+ mWifiNative = wifiNative;
+ mIpconfigStore = ipConfigStore;
+ mPasspointConfigParser = passpointConfigParser;
+ }
+
+ /**
+ * Helper function to lookup the WifiConfiguration object from configKey to WifiConfiguration
+ * object map using the hashcode of the configKey.
+ *
+ * @param configurationMap Map of configKey to WifiConfiguration object.
+ * @param hashCode hash code of the configKey to match.
+ * @return
+ */
+ private static WifiConfiguration lookupWifiConfigurationUsingConfigKeyHash(
+ Map<String, WifiConfiguration> configurationMap, int hashCode) {
+ for (Map.Entry<String, WifiConfiguration> entry : configurationMap.entrySet()) {
+ if (entry.getKey().hashCode() == hashCode) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Helper function to load {@link IpConfiguration} data from the ip config store file and
+ * populate the provided configuration map.
+ *
+ * @param configurationMap Map of configKey to WifiConfiguration object.
+ */
+ private void loadFromIpConfigStore(Map<String, WifiConfiguration> configurationMap) {
+ // This is a map of the hash code of the network's configKey to the corresponding
+ // IpConfiguration.
+ SparseArray<IpConfiguration> ipConfigurations =
+ mIpconfigStore.readIpAndProxyConfigurations(IP_CONFIG_FILE.getAbsolutePath());
+ if (ipConfigurations == null || ipConfigurations.size() == 0) {
+ Log.w(TAG, "No ip configurations found in ipconfig store");
+ return;
+ }
+ for (int i = 0; i < ipConfigurations.size(); i++) {
+ int id = ipConfigurations.keyAt(i);
+ WifiConfiguration config =
+ lookupWifiConfigurationUsingConfigKeyHash(configurationMap, id);
+ // This is the only place the map is looked up through a (dangerous) hash-value!
+ if (config == null || config.ephemeral) {
+ Log.w(TAG, "configuration found for missing network, nid=" + id
+ + ", ignored, networks.size=" + Integer.toString(ipConfigurations.size()));
+ } else {
+ config.setIpConfiguration(ipConfigurations.valueAt(i));
+ }
+ }
+ }
+
+ /**
+ * Helper function to load {@link WifiConfiguration} data from networkHistory file and populate
+ * the provided configuration map and deleted ephemeral ssid list.
+ *
+ * @param configurationMap Map of configKey to WifiConfiguration object.
+ * @param deletedEphemeralSSIDs Map of configKey to WifiConfiguration object.
+ */
+ private void loadFromNetworkHistory(
+ Map<String, WifiConfiguration> configurationMap, Set<String> deletedEphemeralSSIDs) {
+ // TODO: Need to revisit the scan detail cache persistance. We're not doing it in the new
+ // config store, so ignore it here as well.
+ Map<Integer, ScanDetailCache> scanDetailCaches = new HashMap<>();
+ mWifiNetworkHistory.readNetworkHistory(
+ configurationMap, scanDetailCaches, deletedEphemeralSSIDs);
+ }
+
+ /**
+ * Helper function to load {@link WifiConfiguration} data from wpa_supplicant and populate
+ * the provided configuration map and network extras.
+ *
+ * This method needs to manually parse the wpa_supplicant.conf file to retrieve some of the
+ * password fields like psk, wep_keys. password, etc.
+ *
+ * @param configurationMap Map of configKey to WifiConfiguration object.
+ * @param networkExtras Map of network extras parsed from wpa_supplicant.
+ */
+ private void loadFromWpaSupplicant(
+ Map<String, WifiConfiguration> configurationMap,
+ SparseArray<Map<String, String>> networkExtras) {
+ if (!mWifiNative.migrateNetworksFromSupplicant(configurationMap, networkExtras)) {
+ Log.wtf(TAG, "Failed to load wifi configurations from wpa_supplicant");
+ return;
+ }
+ if (configurationMap.isEmpty()) {
+ Log.w(TAG, "No wifi configurations found in wpa_supplicant");
+ return;
+ }
+ }
+
+ /**
+ * Helper function to update {@link WifiConfiguration} that represents a Passpoint
+ * configuration.
+ *
+ * This method will manually parse PerProviderSubscription.conf file to retrieve missing
+ * fields: provider friendly name, roaming consortium OIs, realm, IMSI.
+ *
+ * @param configurationMap Map of configKey to WifiConfiguration object.
+ * @param networkExtras Map of network extras parsed from wpa_supplicant.
+ */
+ private void loadFromPasspointConfigStore(
+ Map<String, WifiConfiguration> configurationMap,
+ SparseArray<Map<String, String>> networkExtras) {
+ Map<String, LegacyPasspointConfig> passpointConfigMap = null;
+ try {
+ passpointConfigMap = mPasspointConfigParser.parseConfig(PPS_FILE.getAbsolutePath());
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to read/parse Passpoint config file: " + e.getMessage());
+ }
+
+ List<String> entriesToBeRemoved = new ArrayList<>();
+ for (Map.Entry<String, WifiConfiguration> entry : configurationMap.entrySet()) {
+ WifiConfiguration wifiConfig = entry.getValue();
+ // Ignore non-Enterprise network since enterprise configuration is required for
+ // Passpoint.
+ if (wifiConfig.enterpriseConfig == null || wifiConfig.enterpriseConfig.getEapMethod()
+ == WifiEnterpriseConfig.Eap.NONE) {
+ continue;
+ }
+ // Ignore configuration without FQDN.
+ Map<String, String> extras = networkExtras.get(wifiConfig.networkId);
+ if (extras == null || !extras.containsKey(SupplicantStaNetworkHal.ID_STRING_KEY_FQDN)) {
+ continue;
+ }
+ String fqdn = networkExtras.get(wifiConfig.networkId).get(
+ SupplicantStaNetworkHal.ID_STRING_KEY_FQDN);
+
+ // Remove the configuration if failed to find the matching configuration in the
+ // Passpoint configuration file.
+ if (passpointConfigMap == null || !passpointConfigMap.containsKey(fqdn)) {
+ entriesToBeRemoved.add(entry.getKey());
+ continue;
+ }
+
+ // Update the missing Passpoint configuration fields to this WifiConfiguration.
+ LegacyPasspointConfig passpointConfig = passpointConfigMap.get(fqdn);
+ wifiConfig.isLegacyPasspointConfig = true;
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = passpointConfig.mFriendlyName;
+ if (passpointConfig.mRoamingConsortiumOis != null) {
+ wifiConfig.roamingConsortiumIds = Arrays.copyOf(
+ passpointConfig.mRoamingConsortiumOis,
+ passpointConfig.mRoamingConsortiumOis.length);
+ }
+ if (passpointConfig.mImsi != null) {
+ wifiConfig.enterpriseConfig.setPlmn(passpointConfig.mImsi);
+ }
+ if (passpointConfig.mRealm != null) {
+ wifiConfig.enterpriseConfig.setRealm(passpointConfig.mRealm);
+ }
+ }
+
+ // Remove any incomplete Passpoint configurations. Should never happen, in case it does
+ // remove them to avoid maintaining any invalid Passpoint configurations.
+ for (String key : entriesToBeRemoved) {
+ Log.w(TAG, "Remove incomplete Passpoint configuration: " + key);
+ configurationMap.remove(key);
+ }
+ }
+
+ /**
+ * Helper function to load from the different legacy stores:
+ * 1. Read the network configurations from wpa_supplicant using {@link WifiNative}.
+ * 2. Read the network configurations from networkHistory.txt using {@link WifiNetworkHistory}.
+ * 3. Read the Ip configurations from ipconfig.txt using {@link IpConfigStore}.
+ * 4. Read all the passpoint info from PerProviderSubscription.conf using
+ * {@link LegacyPasspointConfigParser}.
+ */
+ public WifiConfigStoreDataLegacy read() {
+ final Map<String, WifiConfiguration> configurationMap = new HashMap<>();
+ final SparseArray<Map<String, String>> networkExtras = new SparseArray<>();
+ final Set<String> deletedEphemeralSSIDs = new HashSet<>();
+
+ loadFromWpaSupplicant(configurationMap, networkExtras);
+ loadFromNetworkHistory(configurationMap, deletedEphemeralSSIDs);
+ loadFromIpConfigStore(configurationMap);
+ loadFromPasspointConfigStore(configurationMap, networkExtras);
+
+ // Now create config store data instance to be returned.
+ return new WifiConfigStoreDataLegacy(
+ new ArrayList<>(configurationMap.values()), deletedEphemeralSSIDs);
+ }
+
+ /**
+ * Function to check if the legacy store files are present and hence load from those stores and
+ * then delete them.
+ *
+ * @return true if legacy store files are present, false otherwise.
+ */
+ public boolean areStoresPresent() {
+ // We may have to keep the wpa_supplicant.conf file around. So, just use networkhistory.txt
+ // as a check to see if we have not yet migrated or not. This should be the last file
+ // that is deleted after migration.
+ File file = new File(WifiNetworkHistory.NETWORK_HISTORY_CONFIG_FILE);
+ return file.exists();
+ }
+
+ /**
+ * Method to remove all the legacy store files. This should only be invoked once all
+ * the data has been migrated to the new store file.
+ * 1. Removes all networks from wpa_supplicant and saves it to wpa_supplicant.conf
+ * 2. Deletes ipconfig.txt
+ * 3. Deletes networkHistory.txt
+ *
+ * @return true if all the store files were deleted successfully, false otherwise.
+ */
+ public boolean removeStores() {
+ // TODO(b/29352330): Delete wpa_supplicant.conf file instead.
+ // First remove all networks from wpa_supplicant and save configuration.
+ if (!mWifiNative.removeAllNetworks()) {
+ Log.e(TAG, "Removing networks from wpa_supplicant failed");
+ return false;
+ }
+
+ // Now remove the ipconfig.txt file.
+ if (!IP_CONFIG_FILE.delete()) {
+ Log.e(TAG, "Removing ipconfig.txt failed");
+ return false;
+ }
+
+ // Now finally remove network history.txt
+ if (!NETWORK_HISTORY_FILE.delete()) {
+ Log.e(TAG, "Removing networkHistory.txt failed");
+ return false;
+ }
+
+ if (!PPS_FILE.delete()) {
+ Log.e(TAG, "Removing PerProviderSubscription.conf failed");
+ return false;
+ }
+
+ Log.i(TAG, "All legacy stores removed!");
+ return true;
+ }
+
+ /**
+ * Interface used to set a masked value in the provided configuration. The masked value is
+ * retrieved by parsing the wpa_supplicant.conf file.
+ */
+ private interface MaskedWpaSupplicantFieldSetter {
+ void setValue(WifiConfiguration config, String value);
+ }
+
+ /**
+ * Class used to encapsulate all the store data retrieved from the legacy (Pre O) store files.
+ */
+ public static class WifiConfigStoreDataLegacy {
+ private List<WifiConfiguration> mConfigurations;
+ private Set<String> mDeletedEphemeralSSIDs;
+ // private List<HomeSP> mHomeSps;
+
+ WifiConfigStoreDataLegacy(List<WifiConfiguration> configurations,
+ Set<String> deletedEphemeralSSIDs) {
+ mConfigurations = configurations;
+ mDeletedEphemeralSSIDs = deletedEphemeralSSIDs;
+ }
+
+ public List<WifiConfiguration> getConfigurations() {
+ return mConfigurations;
+ }
+
+ public Set<String> getDeletedEphemeralSSIDs() {
+ return mDeletedEphemeralSSIDs;
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
index 7d0fb3c..c726f49 100644
--- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java
+++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java
@@ -17,33 +17,330 @@
package com.android.server.wifi;
import android.content.pm.UserInfo;
+import android.net.IpConfiguration;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiScanner;
import android.os.UserHandle;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
/**
- * 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 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
+ * profiles
*/
public static boolean isVisibleToAnyProfile(WifiConfiguration config, List<UserInfo> profiles) {
- if (config.shared) {
- return true;
- }
- final int creatorUserId = UserHandle.getUserId(config.creatorUid);
+ return (config.shared || doesUidBelongToAnyProfile(config.creatorUid, profiles));
+ }
+
+ /**
+ * Check whether a uid belong to a user or any of its managed profiles.
+ *
+ * @param uid uid of the app.
+ * @param profiles the user IDs of the user itself and all its managed profiles (can be obtained
+ * via {@link android.os.UserManager#getProfiles})
+ * @return whether the uid belongs to the user or any of its managed profiles.
+ */
+ public static boolean doesUidBelongToAnyProfile(int uid, List<UserInfo> profiles) {
+ final int userId = UserHandle.getUserId(uid);
for (UserInfo profile : profiles) {
- if (profile.id == creatorUserId) {
+ if (profile.id == userId) {
return true;
}
}
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));
+ }
+
+ /**
+ * Compare existing and new WifiConfiguration objects after a network update and return if
+ * IP parameters have changed or not.
+ *
+ * @param existingConfig Existing WifiConfiguration object corresponding to the network.
+ * @param newConfig New WifiConfiguration object corresponding to the network.
+ * @return true if IP parameters have changed, false otherwise.
+ */
+ public static boolean hasIpChanged(WifiConfiguration existingConfig,
+ WifiConfiguration newConfig) {
+ if (existingConfig.getIpAssignment() != newConfig.getIpAssignment()) {
+ return true;
+ }
+ if (newConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) {
+ return !Objects.equals(existingConfig.getStaticIpConfiguration(),
+ newConfig.getStaticIpConfiguration());
+ }
+ return false;
+ }
+
+ /**
+ * Compare existing and new WifiConfiguration objects after a network update and return if
+ * proxy parameters have changed or not.
+ *
+ * @param existingConfig Existing WifiConfiguration object corresponding to the network.
+ * @param newConfig New WifiConfiguration object corresponding to the network.
+ * @return true if proxy parameters have changed, false if no existing config and proxy settings
+ * are NONE, false otherwise.
+ */
+ public static boolean hasProxyChanged(WifiConfiguration existingConfig,
+ WifiConfiguration newConfig) {
+ if (existingConfig == null) {
+ return newConfig.getProxySettings() != IpConfiguration.ProxySettings.NONE;
+ }
+ if (newConfig.getProxySettings() != existingConfig.getProxySettings()) {
+ return true;
+ }
+ return !Objects.equals(existingConfig.getHttpProxy(), newConfig.getHttpProxy());
+ }
+
+ /**
+ * Compare existing and new WifiEnterpriseConfig objects after a network update and return if
+ * credential parameters have changed or not.
+ *
+ * @param existingEnterpriseConfig Existing WifiConfiguration object corresponding to the
+ * network.
+ * @param newEnterpriseConfig New WifiConfiguration object corresponding to the network.
+ * @return true if credentials have changed, false otherwise.
+ */
+ @VisibleForTesting
+ public static boolean hasEnterpriseConfigChanged(WifiEnterpriseConfig existingEnterpriseConfig,
+ WifiEnterpriseConfig newEnterpriseConfig) {
+ if (existingEnterpriseConfig != null && newEnterpriseConfig != null) {
+ if (existingEnterpriseConfig.getEapMethod() != newEnterpriseConfig.getEapMethod()) {
+ return true;
+ }
+ if (existingEnterpriseConfig.getPhase2Method()
+ != newEnterpriseConfig.getPhase2Method()) {
+ return true;
+ }
+ X509Certificate[] existingCaCerts = existingEnterpriseConfig.getCaCertificates();
+ X509Certificate[] newCaCerts = newEnterpriseConfig.getCaCertificates();
+ if (!Arrays.equals(existingCaCerts, newCaCerts)) {
+ return true;
+ }
+ } else {
+ // One of the configs may have an enterpriseConfig
+ if (existingEnterpriseConfig != null || newEnterpriseConfig != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Compare existing and new WifiConfiguration objects after a network update and return if
+ * credential parameters have changed or not.
+ *
+ * @param existingConfig Existing WifiConfiguration object corresponding to the network.
+ * @param newConfig New WifiConfiguration object corresponding to the network.
+ * @return true if credentials have changed, false otherwise.
+ */
+ public static boolean hasCredentialChanged(WifiConfiguration existingConfig,
+ WifiConfiguration newConfig) {
+ if (!Objects.equals(existingConfig.allowedKeyManagement,
+ newConfig.allowedKeyManagement)) {
+ return true;
+ }
+ if (!Objects.equals(existingConfig.allowedProtocols, newConfig.allowedProtocols)) {
+ return true;
+ }
+ if (!Objects.equals(existingConfig.allowedAuthAlgorithms,
+ newConfig.allowedAuthAlgorithms)) {
+ return true;
+ }
+ if (!Objects.equals(existingConfig.allowedPairwiseCiphers,
+ newConfig.allowedPairwiseCiphers)) {
+ return true;
+ }
+ if (!Objects.equals(existingConfig.allowedGroupCiphers,
+ newConfig.allowedGroupCiphers)) {
+ return true;
+ }
+ if (!Objects.equals(existingConfig.preSharedKey, newConfig.preSharedKey)) {
+ return true;
+ }
+ if (!Arrays.equals(existingConfig.wepKeys, newConfig.wepKeys)) {
+ return true;
+ }
+ if (existingConfig.wepTxKeyIndex != newConfig.wepTxKeyIndex) {
+ return true;
+ }
+ if (existingConfig.hiddenSSID != newConfig.hiddenSSID) {
+ return true;
+ }
+ if (hasEnterpriseConfigChanged(existingConfig.enterpriseConfig,
+ newConfig.enterpriseConfig)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if the provided two networks are the same.
+ *
+ * @param config Configuration corresponding to a network.
+ * @param config1 Configuration corresponding to another network.
+ *
+ * @return true if |config| and |config1| are the same network.
+ * false otherwise.
+ */
+ public static boolean isSameNetwork(WifiConfiguration config, WifiConfiguration config1) {
+ if (config == null && config1 == null) {
+ return true;
+ }
+ if (config == null || config1 == null) {
+ return false;
+ }
+ if (config.networkId != config1.networkId) {
+ return false;
+ }
+ if (!Objects.equals(config.SSID, config1.SSID)) {
+ return false;
+ }
+ String networkSelectionBSSID = config.getNetworkSelectionStatus()
+ .getNetworkSelectionBSSID();
+ String networkSelectionBSSID1 = config1.getNetworkSelectionStatus()
+ .getNetworkSelectionBSSID();
+ if (!Objects.equals(networkSelectionBSSID, networkSelectionBSSID1)) {
+ return false;
+ }
+ if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Create a PnoNetwork object from the provided WifiConfiguration.
+ *
+ * @param config Configuration corresponding to the network.
+ * @param newPriority New priority to be assigned to the network.
+ * @return PnoNetwork object corresponding to the network.
+ */
+ public static WifiScanner.PnoSettings.PnoNetwork createPnoNetwork(
+ WifiConfiguration config, int newPriority) {
+ WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
+ new WifiScanner.PnoSettings.PnoNetwork(config.SSID);
+ if (config.hiddenSSID) {
+ pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN;
+ }
+ pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND;
+ pnoNetwork.flags |= WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND;
+ if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK;
+ } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
+ || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
+ pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL;
+ } else {
+ pnoNetwork.authBitField |= WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN;
+ }
+ return pnoNetwork;
+ }
+
+
+ /**
+ * General WifiConfiguration list sorting algorithm:
+ * 1, Place the fully enabled networks first.
+ * 2. Next place all the temporarily disabled networks.
+ * 3. Place the permanently disabled networks last (Permanently disabled networks are removed
+ * before WifiConfigManager uses this comparator today!).
+ *
+ * Among the networks with the same status, sort them in the order determined by the return of
+ * {@link #compareNetworksWithSameStatus(WifiConfiguration, WifiConfiguration)} method
+ * implementation.
+ */
+ public abstract static class WifiConfigurationComparator implements
+ Comparator<WifiConfiguration> {
+ private static final int ENABLED_NETWORK_SCORE = 3;
+ private static final int TEMPORARY_DISABLED_NETWORK_SCORE = 2;
+ private static final int PERMANENTLY_DISABLED_NETWORK_SCORE = 1;
+
+ @Override
+ public int compare(WifiConfiguration a, WifiConfiguration b) {
+ int configAScore = getNetworkStatusScore(a);
+ int configBScore = getNetworkStatusScore(b);
+ if (configAScore == configBScore) {
+ return compareNetworksWithSameStatus(a, b);
+ } else {
+ return Integer.compare(configBScore, configAScore);
+ }
+ }
+
+ // This needs to be implemented by the connected/disconnected PNO list comparator.
+ abstract int compareNetworksWithSameStatus(WifiConfiguration a, WifiConfiguration b);
+
+ /**
+ * Returns an integer representing a score for each configuration. The scores are assigned
+ * based on the status of the configuration. The scores are assigned according to the order:
+ * Fully enabled network > Temporarily disabled network > Permanently disabled network.
+ */
+ private int getNetworkStatusScore(WifiConfiguration config) {
+ if (config.getNetworkSelectionStatus().isNetworkEnabled()) {
+ return ENABLED_NETWORK_SCORE;
+ } else if (config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) {
+ return TEMPORARY_DISABLED_NETWORK_SCORE;
+ } else {
+ return PERMANENTLY_DISABLED_NETWORK_SCORE;
+ }
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiConnectivityHelper.java b/service/java/com/android/server/wifi/WifiConnectivityHelper.java
new file mode 100644
index 0000000..4aac311
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiConnectivityHelper.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static android.net.wifi.WifiManager.WIFI_FEATURE_CONTROL_ROAMING;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+
+/**
+ * This class provides helper functions for Wifi connectivity related modules to
+ * access WifiNative. It starts with firmware roaming. TODO(b/34819513): Move operations
+ * such as connection to network and legacy framework roaming here.
+ *
+ * NOTE: This class is not thread safe and should only be used from the WifiStateMachine thread.
+ */
+public class WifiConnectivityHelper {
+ private static final String TAG = "WifiConnectivityHelper";
+ @VisibleForTesting
+ public static int INVALID_LIST_SIZE = -1;
+ private final WifiNative mWifiNative;
+ private boolean mFirmwareRoamingSupported = false;
+ private int mMaxNumBlacklistBssid = INVALID_LIST_SIZE;
+ private int mMaxNumWhitelistSsid = INVALID_LIST_SIZE;
+
+ WifiConnectivityHelper(WifiNative wifiNative) {
+ mWifiNative = wifiNative;
+ }
+
+ /**
+ * Query firmware if it supports
+ * {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING}. If yes, get the firmware
+ * roaming capabilities. If firmware roaming is supported but we fail to get the roaming
+ * capabilities or the returned capability values are invalid, we fall back to framework
+ * roaming.
+ *
+ * @return true if succeed, false if firmware roaming is supported but fail to get valid
+ * roaming capabilities.
+ */
+ public boolean getFirmwareRoamingInfo() {
+ mFirmwareRoamingSupported = false;
+ mMaxNumBlacklistBssid = INVALID_LIST_SIZE;
+ mMaxNumWhitelistSsid = INVALID_LIST_SIZE;
+
+ int fwFeatureSet = mWifiNative.getSupportedFeatureSet();
+ Log.d(TAG, "Firmware supported feature set: " + Integer.toHexString(fwFeatureSet));
+
+ if ((fwFeatureSet & WIFI_FEATURE_CONTROL_ROAMING) == 0) {
+ Log.d(TAG, "Firmware roaming is not supported");
+ return true;
+ }
+
+ WifiNative.RoamingCapabilities roamingCap = new WifiNative.RoamingCapabilities();
+ if (mWifiNative.getRoamingCapabilities(roamingCap)) {
+ if (roamingCap.maxBlacklistSize < 0 || roamingCap.maxWhitelistSize < 0) {
+ Log.e(TAG, "Invalid firmware roaming capabilities: max num blacklist bssid="
+ + roamingCap.maxBlacklistSize + " max num whitelist ssid="
+ + roamingCap.maxWhitelistSize);
+ } else {
+ mFirmwareRoamingSupported = true;
+ mMaxNumBlacklistBssid = roamingCap.maxBlacklistSize;
+ mMaxNumWhitelistSsid = roamingCap.maxWhitelistSize;
+ Log.d(TAG, "Firmware roaming supported with capabilities: max num blacklist bssid="
+ + mMaxNumBlacklistBssid + " max num whitelist ssid="
+ + mMaxNumWhitelistSsid);
+ return true;
+ }
+ } else {
+ Log.e(TAG, "Failed to get firmware roaming capabilities");
+ }
+
+ return false;
+ }
+
+ /**
+ * Return if firmware roaming is supported.
+ */
+ public boolean isFirmwareRoamingSupported() {
+ return mFirmwareRoamingSupported;
+ }
+
+ /**
+ * Get the maximum size of BSSID blacklist firmware supports.
+ *
+ * @return INVALID_LIST_SIZE if firmware roaming is not supported, or
+ * maximum size of the BSSID blacklist firmware supports.
+ */
+ public int getMaxNumBlacklistBssid() {
+ if (mFirmwareRoamingSupported) {
+ return mMaxNumBlacklistBssid;
+ } else {
+ Log.e(TAG, "getMaxNumBlacklistBssid: Firmware roaming is not supported");
+ return INVALID_LIST_SIZE;
+ }
+ }
+
+ /**
+ * Get the maximum size of SSID whitelist firmware supports.
+ *
+ * @return INVALID_LIST_SIZE if firmware roaming is not supported, or
+ * maximum size of the SSID whitelist firmware supports.
+ */
+ public int getMaxNumWhitelistSsid() {
+ if (mFirmwareRoamingSupported) {
+ return mMaxNumWhitelistSsid;
+ } else {
+ Log.e(TAG, "getMaxNumWhitelistSsid: Firmware roaming is not supported");
+ return INVALID_LIST_SIZE;
+ }
+ }
+
+ /**
+ * Write firmware roaming configuration to firmware.
+ *
+ * @param blacklistBssids BSSIDs to be blacklisted
+ * @param whitelistSsids SSIDs to be whitelisted
+ * @return true if succeeded, false otherwise.
+ */
+ public boolean setFirmwareRoamingConfiguration(ArrayList<String> blacklistBssids,
+ ArrayList<String> whitelistSsids) {
+ if (!mFirmwareRoamingSupported) {
+ Log.e(TAG, "Firmware roaming is not supported");
+ return false;
+ }
+
+ if (blacklistBssids == null || whitelistSsids == null) {
+ Log.e(TAG, "Invalid firmware roaming configuration settings");
+ return false;
+ }
+
+ int blacklistSize = blacklistBssids.size();
+ int whitelistSize = whitelistSsids.size();
+
+ if (blacklistSize > mMaxNumBlacklistBssid || whitelistSize > mMaxNumWhitelistSsid) {
+ Log.e(TAG, "Invalid BSSID blacklist size " + blacklistSize + " SSID whitelist size "
+ + whitelistSize + ". Max blacklist size: " + mMaxNumBlacklistBssid
+ + ", max whitelist size: " + mMaxNumWhitelistSsid);
+ return false;
+ }
+
+ WifiNative.RoamingConfig roamConfig = new WifiNative.RoamingConfig();
+ roamConfig.blacklistBssids = blacklistBssids;
+ roamConfig.whitelistSsids = whitelistSsids;
+
+ return mWifiNative.configureRoaming(roamConfig);
+ }
+
+ /**
+ * Remove the request |networkId| from supplicant if it's the current network,
+ * if the current configured network matches |networkId|.
+ *
+ * @param networkId network id of the network to be removed from supplicant.
+ */
+ public void removeNetworkIfCurrent(int networkId) {
+ mWifiNative.removeNetworkIfCurrent(networkId);
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index 49a2842..f45d17b 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -18,14 +18,12 @@
import static com.android.server.wifi.WifiStateMachine.WIFI_WORK_SOURCE;
-import android.app.ActivityManager;
import android.app.AlarmManager;
import android.content.Context;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiScanner.PnoSettings;
import android.net.wifi.WifiScanner.ScanSettings;
@@ -36,15 +34,18 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wifi.util.ScanDetailUtil;
+import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator;
+import com.android.server.wifi.util.ScanResultUtil;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -52,7 +53,8 @@
*
* When the screen is turned on or off, WiFi is connected or disconnected,
* or on-demand, a scan is initiatiated and the scan results are passed
- * to QNS for it to make a recommendation on which network to connect to.
+ * to WifiNetworkSelector for it to make a recommendation on which network
+ * to connect to.
*/
public class WifiConnectivityManager {
public static final String WATCHDOG_TIMER_TAG =
@@ -64,7 +66,6 @@
public static final String RESTART_CONNECTIVITY_SCAN_TIMER_TAG =
"WifiConnectivityManager Restart Scan";
- private static final String TAG = "WifiConnectivityManager";
private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
// Constants to indicate whether a scan should start immediately or
// it should comply to the minimum scan interval rule.
@@ -85,8 +86,8 @@
// PNO scan interval in milli-seconds. This is the scan
// performed when screen is off and connected.
private static final int CONNECTED_PNO_SCAN_INTERVAL_MS = 160 * 1000; // 160 seconds
- // When a network is found by PNO scan but gets rejected by QNS due to its
- // low RSSI value, scan will be reschduled in an exponential back off manner.
+ // When a network is found by PNO scan but gets rejected by Wifi Network Selector due
+ // to its low RSSI value, scan will be reschduled in an exponential back off manner.
private static final int LOW_RSSI_NETWORK_RETRY_START_DELAY_MS = 20 * 1000; // 20 seconds
private static final int LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS = 80 * 1000; // 80 seconds
// Maximum number of retries when starting a scan failed
@@ -106,6 +107,10 @@
public static final int MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS = 4 * 60 * 1000; // 4 mins
// Max number of connection attempts in the above time interval.
public static final int MAX_CONNECTION_ATTEMPTS_RATE = 6;
+ // Packet tx/rx rates to determine if we want to do partial vs full scans.
+ // TODO(b/31180330): Make these device configs.
+ public static final int MAX_TX_PACKET_FOR_FULL_SCANS = 8;
+ public static final int MAX_RX_PACKET_FOR_FULL_SCANS = 16;
// WifiStateMachine has a bunch of states. From the
// WifiConnectivityManager's perspective it only cares
@@ -116,24 +121,26 @@
public static final int WIFI_STATE_DISCONNECTED = 2;
public static final int WIFI_STATE_TRANSITIONING = 3;
- // Due to b/28020168, timer based single scan will be scheduled
- // to provide periodic scan in an exponential backoff fashion.
- private static final boolean ENABLE_BACKGROUND_SCAN = false;
- // Flag to turn on connected PNO, when needed
- private static final boolean ENABLE_CONNECTED_PNO_SCAN = false;
+ // Saved network evaluator priority
+ private static final int SAVED_NETWORK_EVALUATOR_PRIORITY = 1;
+ private static final int PASSPOINT_NETWORK_EVALUATOR_PRIORITY = 2;
+ private static final int SCORED_NETWORK_EVALUATOR_PRIORITY = 3;
+
+ // Log tag for this class
+ private static final String TAG = "WifiConnectivityManager";
private final WifiStateMachine mStateMachine;
private final WifiScanner mScanner;
private final WifiConfigManager mConfigManager;
private final WifiInfo mWifiInfo;
- private final WifiQualifiedNetworkSelector mQualifiedNetworkSelector;
+ private final WifiConnectivityHelper mConnectivityHelper;
+ private final WifiNetworkSelector mNetworkSelector;
private final WifiLastResortWatchdog mWifiLastResortWatchdog;
private final WifiMetrics mWifiMetrics;
private final AlarmManager mAlarmManager;
private final Handler mEventHandler;
private final Clock mClock;
- private final LocalLog mLocalLog =
- new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 128 : 256);
+ private final LocalLog mLocalLog;
private final LinkedList<Long> mConnectionAttemptTimeStamps;
private boolean mDbg = false;
@@ -150,6 +157,8 @@
private long mLastPeriodicSingleScanTimeStamp = RESET_TIME_STAMP;
private boolean mPnoScanStarted = false;
private boolean mPeriodicScanTimerSet = false;
+ // Device configs
+ private boolean mEnableAutoJoinWhenAssociated;
private boolean mWaitForFullBandScanResults = false;
// PNO settings
@@ -161,6 +170,24 @@
private int mSecureBonus;
private int mBand5GHzBonus;
+ // BSSID blacklist
+ @VisibleForTesting
+ public static final int BSSID_BLACKLIST_THRESHOLD = 3;
+ @VisibleForTesting
+ public static final int BSSID_BLACKLIST_EXPIRE_TIME_MS = 5 * 60 * 1000;
+ private static class BssidBlacklistStatus {
+ // Number of times this BSSID has been rejected for association.
+ public int counter;
+ public boolean isBlacklisted;
+ public long blacklistedTimeStamp = RESET_TIME_STAMP;
+ }
+ private Map<String, BssidBlacklistStatus> mBssidBlacklist =
+ new HashMap<>();
+
+ // Association failure reason codes
+ @VisibleForTesting
+ public static final int REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
+
// A helper to log debugging information in the local log buffer, which can
// be retrieved in bugreport.
private void localLog(String log) {
@@ -214,22 +241,31 @@
* Executes selection of potential network candidates, initiation of connection attempt to that
* network.
*
- * @return true - if a candidate is selected by QNS
- * false - if no candidate is selected by QNS
+ * @return true - if a candidate is selected by WifiNetworkSelector
+ * false - if no candidate is selected by WifiNetworkSelector
*/
private boolean handleScanResults(List<ScanDetail> scanDetails, String listenerName) {
- localLog(listenerName + " onResults: start QNS");
+ // Check if any blacklisted BSSIDs can be freed.
+ refreshBssidBlacklist();
+
+ if (mStateMachine.isLinkDebouncing() || mStateMachine.isSupplicantTransientState()) {
+ localLog(listenerName + " onResults: No network selection because linkDebouncing is "
+ + mStateMachine.isLinkDebouncing() + " and supplicantTransient is "
+ + mStateMachine.isSupplicantTransientState());
+ return false;
+ }
+
+ localLog(listenerName + " onResults: start network selection");
+
WifiConfiguration candidate =
- mQualifiedNetworkSelector.selectQualifiedNetwork(false,
- mUntrustedConnectionAllowed, scanDetails,
- mStateMachine.isLinkDebouncing(), mStateMachine.isConnected(),
- mStateMachine.isDisconnected(),
- mStateMachine.isSupplicantTransientState());
+ mNetworkSelector.selectNetwork(scanDetails, buildBssidBlacklist(), mWifiInfo,
+ mStateMachine.isConnected(), mStateMachine.isDisconnected(),
+ mUntrustedConnectionAllowed);
mWifiLastResortWatchdog.updateAvailableNetworks(
- mQualifiedNetworkSelector.getFilteredScanDetails());
+ mNetworkSelector.getFilteredScanDetails());
mWifiMetrics.countScanResults(scanDetails);
if (candidate != null) {
- localLog(listenerName + ": QNS candidate-" + candidate.SSID);
+ localLog(listenerName + ": WNS candidate-" + candidate.SSID);
connectToNetwork(candidate);
return true;
} else {
@@ -237,63 +273,6 @@
}
}
- // Periodic scan results listener. A periodic scan is initiated when
- // screen is on.
- private class PeriodicScanListener implements WifiScanner.ScanListener {
- private List<ScanDetail> mScanDetails = new ArrayList<ScanDetail>();
-
- public void clearScanDetails() {
- mScanDetails.clear();
- }
-
- @Override
- public void onSuccess() {
- localLog("PeriodicScanListener onSuccess");
- }
-
- @Override
- public void onFailure(int reason, String description) {
- Log.e(TAG, "PeriodicScanListener onFailure:"
- + " reason: " + reason
- + " description: " + description);
-
- // reschedule the scan
- if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
- scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS);
- } else {
- mScanRestartCount = 0;
- Log.e(TAG, "Failed to successfully start periodic scan for "
- + MAX_SCAN_RESTART_ALLOWED + " times");
- }
- }
-
- @Override
- public void onPeriodChanged(int periodInMs) {
- localLog("PeriodicScanListener onPeriodChanged: "
- + "actual scan period " + periodInMs + "ms");
- }
-
- @Override
- public void onResults(WifiScanner.ScanData[] results) {
- handleScanResults(mScanDetails, "PeriodicScanListener");
- clearScanDetails();
- mScanRestartCount = 0;
- }
-
- @Override
- public void onFullResult(ScanResult fullScanResult) {
- if (mDbg) {
- localLog("PeriodicScanListener onFullResult: "
- + fullScanResult.SSID + " capabilities "
- + fullScanResult.capabilities);
- }
-
- mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult));
- }
- }
-
- private final PeriodicScanListener mPeriodicScanListener = new PeriodicScanListener();
-
// All single scan results listener.
//
// Note: This is the listener for all the available single scan results,
@@ -308,14 +287,12 @@
@Override
public void onSuccess() {
- localLog("registerScanListener onSuccess");
}
@Override
public void onFailure(int reason, String description) {
- Log.e(TAG, "registerScanListener onFailure:"
- + " reason: " + reason
- + " description: " + description);
+ localLog("registerScanListener onFailure:"
+ + " reason: " + reason + " description: " + description);
}
@Override
@@ -364,19 +341,18 @@
}
if (mDbg) {
- localLog("AllSingleScanListener onFullResult: "
- + fullScanResult.SSID + " capabilities "
- + fullScanResult.capabilities);
+ localLog("AllSingleScanListener onFullResult: " + fullScanResult.SSID
+ + " capabilities " + fullScanResult.capabilities);
}
- mScanDetails.add(ScanDetailUtil.toScanDetail(fullScanResult));
+ mScanDetails.add(ScanResultUtil.toScanDetail(fullScanResult));
}
}
private final AllSingleScanListener mAllSingleScanListener = new AllSingleScanListener();
// Single scan results listener. A single scan is initiated when
- // Disconnected/ConnectedPNO scan found a valid network and woke up
+ // DisconnectedPNO scan found a valid network and woke up
// the system, or by the watchdog timer, or to form the timer based
// periodic scan.
//
@@ -391,29 +367,27 @@
@Override
public void onSuccess() {
- localLog("SingleScanListener onSuccess");
}
@Override
public void onFailure(int reason, String description) {
- Log.e(TAG, "SingleScanListener onFailure:"
- + " reason: " + reason
- + " description: " + description);
+ localLog("SingleScanListener onFailure:"
+ + " reason: " + reason + " description: " + description);
// reschedule the scan
if (mSingleScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
scheduleDelayedSingleScan(mIsFullBandScan);
} else {
mSingleScanRestartCount = 0;
- Log.e(TAG, "Failed to successfully start single scan for "
- + MAX_SCAN_RESTART_ALLOWED + " times");
+ localLog("Failed to successfully start single scan for "
+ + MAX_SCAN_RESTART_ALLOWED + " times");
}
}
@Override
public void onPeriodChanged(int periodInMs) {
localLog("SingleScanListener onPeriodChanged: "
- + "actual scan period " + periodInMs + "ms");
+ + "actual scan period " + periodInMs + "ms");
}
@Override
@@ -425,9 +399,6 @@
}
}
- // re-enable this when b/27695292 is fixed
- // private final SingleScanListener mSingleScanListener = new SingleScanListener();
-
// PNO scan results listener for both disconected and connected PNO scanning.
// A PNO scan is initiated when screen is off.
private class PnoScanListener implements WifiScanner.PnoScanListener {
@@ -440,7 +411,7 @@
}
// Reset to the start value when either a non-PNO scan is started or
- // QNS selects a candidate from the PNO scan results.
+ // WifiNetworkSelector selects a candidate from the PNO scan results.
public void resetLowRssiNetworkRetryDelay() {
mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_START_DELAY_MS;
}
@@ -452,33 +423,31 @@
@Override
public void onSuccess() {
- localLog("PnoScanListener onSuccess");
}
@Override
public void onFailure(int reason, String description) {
- Log.e(TAG, "PnoScanListener onFailure:"
- + " reason: " + reason
- + " description: " + description);
+ localLog("PnoScanListener onFailure:"
+ + " reason: " + reason + " description: " + description);
// reschedule the scan
if (mScanRestartCount++ < MAX_SCAN_RESTART_ALLOWED) {
scheduleDelayedConnectivityScan(RESTART_SCAN_DELAY_MS);
} else {
mScanRestartCount = 0;
- Log.e(TAG, "Failed to successfully start PNO scan for "
- + MAX_SCAN_RESTART_ALLOWED + " times");
+ localLog("Failed to successfully start PNO scan for "
+ + MAX_SCAN_RESTART_ALLOWED + " times");
}
}
@Override
public void onPeriodChanged(int periodInMs) {
localLog("PnoScanListener onPeriodChanged: "
- + "actual scan period " + periodInMs + "ms");
+ + "actual scan period " + periodInMs + "ms");
}
// Currently the PNO scan results doesn't include IE,
- // which contains information required by QNS. Ignore them
+ // which contains information required by WifiNetworkSelector. Ignore them
// for now.
@Override
public void onResults(WifiScanner.ScanData[] results) {
@@ -490,10 +459,8 @@
@Override
public void onPnoNetworkFound(ScanResult[] results) {
- localLog("PnoScanListener: onPnoNetworkFound: results len = " + results.length);
-
for (ScanResult result: results) {
- mScanDetails.add(ScanDetailUtil.toScanDetail(result));
+ mScanDetails.add(ScanResultUtil.toScanDetail(result));
}
boolean wasConnectAttempted;
@@ -502,7 +469,7 @@
mScanRestartCount = 0;
if (!wasConnectAttempted) {
- // The scan results were rejected by QNS due to low RSSI values
+ // The scan results were rejected by WifiNetworkSelector due to low RSSI values
if (mLowRssiNetworkRetryDelay > LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS) {
mLowRssiNetworkRetryDelay = LOW_RSSI_NETWORK_RETRY_MAX_DELAY_MS;
}
@@ -518,50 +485,122 @@
private final PnoScanListener mPnoScanListener = new PnoScanListener();
+ private class OnSavedNetworkUpdateListener implements
+ WifiConfigManager.OnSavedNetworkUpdateListener {
+ @Override
+ public void onSavedNetworkAdded(int networkId) {
+ updatePnoScan();
+ }
+ @Override
+ public void onSavedNetworkEnabled(int networkId) {
+ updatePnoScan();
+ }
+ @Override
+ public void onSavedNetworkRemoved(int networkId) {
+ updatePnoScan();
+ }
+ @Override
+ public void onSavedNetworkUpdated(int networkId) {
+ updatePnoScan();
+ }
+ @Override
+ public void onSavedNetworkTemporarilyDisabled(int networkId) {
+ mConnectivityHelper.removeNetworkIfCurrent(networkId);
+ }
+ @Override
+ public void onSavedNetworkPermanentlyDisabled(int networkId) {
+ mConnectivityHelper.removeNetworkIfCurrent(networkId);
+ updatePnoScan();
+ }
+ private void updatePnoScan() {
+ // Update the PNO scan network list when screen is off. Here we
+ // rely on startConnectivityScan() to perform all the checks and clean up.
+ if (!mScreenOn) {
+ localLog("Saved networks updated");
+ startConnectivityScan(false);
+ }
+ }
+ }
+
/**
* WifiConnectivityManager constructor
*/
- public WifiConnectivityManager(Context context, WifiStateMachine stateMachine,
- WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo,
- WifiQualifiedNetworkSelector qualifiedNetworkSelector,
- WifiInjector wifiInjector, Looper looper, boolean enable) {
+ WifiConnectivityManager(Context context, WifiStateMachine stateMachine,
+ WifiScanner scanner, WifiConfigManager configManager, WifiInfo wifiInfo,
+ WifiNetworkSelector networkSelector, WifiConnectivityHelper connectivityHelper,
+ WifiLastResortWatchdog wifiLastResortWatchdog, WifiMetrics wifiMetrics,
+ Looper looper, Clock clock, LocalLog localLog, boolean enable,
+ FrameworkFacade frameworkFacade,
+ SavedNetworkEvaluator savedNetworkEvaluator,
+ ScoredNetworkEvaluator scoredNetworkEvaluator,
+ PasspointNetworkEvaluator passpointNetworkEvaluator) {
mStateMachine = stateMachine;
mScanner = scanner;
mConfigManager = configManager;
mWifiInfo = wifiInfo;
- mQualifiedNetworkSelector = qualifiedNetworkSelector;
- mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
- mWifiMetrics = wifiInjector.getWifiMetrics();
+ mNetworkSelector = networkSelector;
+ mConnectivityHelper = connectivityHelper;
+ mLocalLog = localLog;
+ mWifiLastResortWatchdog = wifiLastResortWatchdog;
+ mWifiMetrics = wifiMetrics;
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mEventHandler = new Handler(looper);
- mClock = wifiInjector.getClock();
+ mClock = clock;
mConnectionAttemptTimeStamps = new LinkedList<>();
- mMin5GHzRssi = WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI;
- mMin24GHzRssi = WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI;
- mBand5GHzBonus = WifiQualifiedNetworkSelector.BAND_AWARD_5GHz;
- mCurrentConnectionBonus = mConfigManager.mCurrentNetworkBoost.get();
+ mMin5GHzRssi = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
+ mMin24GHzRssi = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
+ mBand5GHzBonus = context.getResources().getInteger(
+ R.integer.config_wifi_framework_5GHz_preference_boost_factor);
+ mCurrentConnectionBonus = context.getResources().getInteger(
+ R.integer.config_wifi_framework_current_network_boost);
mSameNetworkBonus = context.getResources().getInteger(
- R.integer.config_wifi_framework_SAME_BSSID_AWARD);
+ R.integer.config_wifi_framework_SAME_BSSID_AWARD);
mSecureBonus = context.getResources().getInteger(
- R.integer.config_wifi_framework_SECURITY_AWARD);
- mInitialScoreMax = (mConfigManager.mThresholdSaturatedRssi24.get()
- + WifiQualifiedNetworkSelector.RSSI_SCORE_OFFSET)
- * WifiQualifiedNetworkSelector.RSSI_SCORE_SLOPE;
+ R.integer.config_wifi_framework_SECURITY_AWARD);
+ int thresholdSaturatedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
+ mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
+ R.bool.config_wifi_framework_enable_associated_network_selection);
+ mInitialScoreMax = (context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz)
+ + context.getResources().getInteger(
+ R.integer.config_wifi_framework_RSSI_SCORE_OFFSET))
+ * context.getResources().getInteger(
+ R.integer.config_wifi_framework_RSSI_SCORE_SLOPE);
- Log.i(TAG, "PNO settings:" + " min5GHzRssi " + mMin5GHzRssi
- + " min24GHzRssi " + mMin24GHzRssi
- + " currentConnectionBonus " + mCurrentConnectionBonus
- + " sameNetworkBonus " + mSameNetworkBonus
- + " secureNetworkBonus " + mSecureBonus
- + " initialScoreMax " + mInitialScoreMax);
+ localLog("PNO settings:" + " min5GHzRssi " + mMin5GHzRssi
+ + " min24GHzRssi " + mMin24GHzRssi
+ + " currentConnectionBonus " + mCurrentConnectionBonus
+ + " sameNetworkBonus " + mSameNetworkBonus
+ + " secureNetworkBonus " + mSecureBonus
+ + " initialScoreMax " + mInitialScoreMax);
+
+ boolean hs2Enabled = context.getResources().getBoolean(
+ R.bool.config_wifi_hotspot2_enabled);
+ localLog("Passpoint is: " + (hs2Enabled ? "enabled" : "disabled"));
+
+ // Register the network evaluators
+ mNetworkSelector.registerNetworkEvaluator(savedNetworkEvaluator,
+ SAVED_NETWORK_EVALUATOR_PRIORITY);
+ if (hs2Enabled) {
+ mNetworkSelector.registerNetworkEvaluator(passpointNetworkEvaluator,
+ PASSPOINT_NETWORK_EVALUATOR_PRIORITY);
+ }
+ mNetworkSelector.registerNetworkEvaluator(scoredNetworkEvaluator,
+ SCORED_NETWORK_EVALUATOR_PRIORITY);
// Register for all single scan results
mScanner.registerScanListener(mAllSingleScanListener);
+ // Listen to WifiConfigManager network update events
+ mConfigManager.setOnSavedNetworkUpdateListener(new OnSavedNetworkUpdateListener());
+
mWifiConnectivityManagerEnabled = enable;
- Log.i(TAG, "ConnectivityScanManager initialized and "
+ localLog("ConnectivityScanManager initialized and "
+ (enable ? "enabled" : "disabled"));
}
@@ -606,12 +645,12 @@
* Attempt to connect to a network candidate.
*
* Based on the currently connected network, this menthod determines whether we should
- * connect or roam to the network candidate recommended by QNS.
+ * connect or roam to the network candidate recommended by WifiNetworkSelector.
*/
private void connectToNetwork(WifiConfiguration candidate) {
ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
if (scanResultCandidate == null) {
- Log.e(TAG, "connectToNetwork: bad candidate - " + candidate
+ localLog("connectToNetwork: bad candidate - " + candidate
+ " scanResult: " + scanResultCandidate);
return;
}
@@ -621,8 +660,8 @@
// Check if we are already connected or in the process of connecting to the target
// BSSID. mWifiInfo.mBSSID tracks the currently connected BSSID. This is checked just
- // in case the firmware automatically roamed to a BSSID different from what QNS
- // selected.
+ // in case the firmware automatically roamed to a BSSID different from what
+ // WifiNetworkSelector selected.
if (targetBssid != null
&& (targetBssid.equals(mLastConnectionAttemptBssid)
|| targetBssid.equals(mWifiInfo.getBSSID()))
@@ -632,7 +671,15 @@
return;
}
- Long elapsedTimeMillis = mClock.elapsedRealtime();
+ if (candidate.BSSID != null
+ && !candidate.BSSID.equals(WifiStateMachine.SUPPLICANT_BSSID_ANY)
+ && !candidate.BSSID.equals(targetBssid)) {
+ localLog("connecToNetwork: target BSSID " + targetBssid + " does not match the "
+ + "config specified BSSID " + candidate.BSSID + ". Drop it!");
+ return;
+ }
+
+ Long elapsedTimeMillis = mClock.getElapsedSinceBootMillis();
if (!mScreenOn && shouldSkipConnectionAttempt(elapsedTimeMillis)) {
localLog("connectToNetwork: Too many connection attempts. Skipping this attempt!");
mTotalConnectivityAttemptsRateLimited++;
@@ -643,20 +690,39 @@
mLastConnectionAttemptBssid = targetBssid;
WifiConfiguration currentConnectedNetwork = mConfigManager
- .getWifiConfiguration(mWifiInfo.getNetworkId());
+ .getConfiguredNetwork(mWifiInfo.getNetworkId());
String currentAssociationId = (currentConnectedNetwork == null) ? "Disconnected" :
(mWifiInfo.getSSID() + " : " + mWifiInfo.getBSSID());
if (currentConnectedNetwork != null
&& (currentConnectedNetwork.networkId == candidate.networkId
- || currentConnectedNetwork.isLinked(candidate))) {
- localLog("connectToNetwork: Roaming from " + currentAssociationId + " to "
- + targetAssociationId);
- mStateMachine.autoRoamToNetwork(candidate.networkId, scanResultCandidate);
+ //TODO(b/36788683): re-enable linked configuration check
+ /* || currentConnectedNetwork.isLinked(candidate) */)) {
+ // Framework initiates roaming only if firmware doesn't support
+ // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING}.
+ if (mConnectivityHelper.isFirmwareRoamingSupported()) {
+ // Keep this logging here for now to validate the firmware roaming behavior.
+ localLog("connectToNetwork: Roaming candidate - " + targetAssociationId + "."
+ + " The actual roaming target is up to the firmware.");
+ } else {
+ localLog("connectToNetwork: Roaming to " + targetAssociationId + " from "
+ + currentAssociationId);
+ mStateMachine.startRoamToNetwork(candidate.networkId, scanResultCandidate);
+ }
} else {
- localLog("connectToNetwork: Reconnect from " + currentAssociationId + " to "
- + targetAssociationId);
- mStateMachine.autoConnectToNetwork(candidate.networkId, scanResultCandidate.BSSID);
+ // Framework specifies the connection target BSSID if firmware doesn't support
+ // {@link android.net.wifi.WifiManager#WIFI_FEATURE_CONTROL_ROAMING} or the
+ // candidate configuration contains a specified BSSID.
+ if (mConnectivityHelper.isFirmwareRoamingSupported() && (candidate.BSSID == null
+ || candidate.BSSID.equals(WifiStateMachine.SUPPLICANT_BSSID_ANY))) {
+ targetBssid = WifiStateMachine.SUPPLICANT_BSSID_ANY;
+ localLog("connectToNetwork: Connect to " + candidate.SSID + ":" + targetBssid
+ + " from " + currentAssociationId);
+ } else {
+ localLog("connectToNetwork: Connect to " + targetAssociationId + " from "
+ + currentAssociationId);
+ }
+ mStateMachine.startConnectToNetwork(candidate.networkId, targetBssid);
}
}
@@ -667,14 +733,7 @@
private int getScanBand(boolean isFullBandScan) {
if (isFullBandScan) {
- int freqBand = mStateMachine.getFrequencyBand();
- if (freqBand == WifiManager.WIFI_FREQUENCY_BAND_5GHZ) {
- return WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS;
- } else if (freqBand == WifiManager.WIFI_FREQUENCY_BAND_2GHZ) {
- return WifiScanner.WIFI_BAND_24_GHZ;
- } else {
- return WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
- }
+ return WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
} else {
// Use channel list instead.
return WifiScanner.WIFI_BAND_UNSPECIFIED;
@@ -690,7 +749,9 @@
return false;
}
- HashSet<Integer> freqs = mConfigManager.makeChannelList(config, CHANNEL_LIST_AGE_MS);
+ Set<Integer> freqs =
+ mConfigManager.fetchChannelSetForNetworkForPartialScan(
+ config.networkId, CHANNEL_LIST_AGE_MS, mWifiInfo.getFrequency());
if (freqs != null && freqs.size() != 0) {
int index = 0;
@@ -707,13 +768,11 @@
// Watchdog timer handler
private void watchdogHandler() {
- localLog("watchdogHandler");
-
// Schedule the next timer and start a single scan if we are in disconnected state.
// Otherwise, the watchdog timer will be scheduled when entering disconnected
// state.
if (mWifiState == WIFI_STATE_DISCONNECTED) {
- Log.i(TAG, "start a single scan from watchdogHandler");
+ localLog("start a single scan from watchdogHandler");
scheduleWatchdogTimer();
startSingleScan(true);
@@ -722,7 +781,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;
@@ -738,13 +797,9 @@
// If the WiFi traffic is heavy, only partial scan is initiated.
if (mWifiState == WIFI_STATE_CONNECTED
- && (mWifiInfo.txSuccessRate
- > mConfigManager.MAX_TX_PACKET_FOR_FULL_SCANS
- || mWifiInfo.rxSuccessRate
- > mConfigManager.MAX_RX_PACKET_FOR_FULL_SCANS)) {
- localLog("No full band scan due to heavy traffic, txSuccessRate="
- + mWifiInfo.txSuccessRate + " rxSuccessRate="
- + mWifiInfo.rxSuccessRate);
+ && (mWifiInfo.txSuccessRate > MAX_TX_PACKET_FOR_FULL_SCANS
+ || mWifiInfo.rxSuccessRate > MAX_RX_PACKET_FOR_FULL_SCANS)) {
+ localLog("No full band scan due to ongoing traffic");
isFullBandScan = false;
}
@@ -794,19 +849,11 @@
| WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
settings.numBssidsPerScan = 0;
- //Retrieve the list of hidden networkId's to scan for.
- Set<Integer> hiddenNetworkIds = mConfigManager.getHiddenConfiguredNetworkIds();
- if (hiddenNetworkIds != null && hiddenNetworkIds.size() > 0) {
- int i = 0;
- settings.hiddenNetworkIds = new int[hiddenNetworkIds.size()];
- for (Integer netId : hiddenNetworkIds) {
- settings.hiddenNetworkIds[i++] = netId;
- }
- }
+ List<ScanSettings.HiddenNetwork> hiddenNetworkList =
+ mConfigManager.retrieveHiddenNetworkList();
+ settings.hiddenNetworks =
+ hiddenNetworkList.toArray(new ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
- // re-enable this when b/27695292 is fixed
- // mSingleScanListener.clearScanDetails();
- // mScanner.startScan(settings, mSingleScanListener, WIFI_WORK_SOURCE);
SingleScanListener singleScanListener =
new SingleScanListener(isFullBandScan);
mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE);
@@ -817,38 +864,26 @@
mPnoScanListener.resetLowRssiNetworkRetryDelay();
// No connectivity scan if auto roaming is disabled.
- if (mWifiState == WIFI_STATE_CONNECTED
- && !mConfigManager.getEnableAutoJoinWhenAssociated()) {
+ if (mWifiState == WIFI_STATE_CONNECTED && !mEnableAutoJoinWhenAssociated) {
return;
}
// Due to b/28020168, timer based single scan will be scheduled
// to provide periodic scan in an exponential backoff fashion.
- if (!ENABLE_BACKGROUND_SCAN) {
- if (scanImmediately) {
- resetLastPeriodicSingleScanTimeStamp();
- }
- mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
- startPeriodicSingleScan();
- } else {
- ScanSettings settings = new ScanSettings();
- settings.band = getScanBand();
- settings.reportEvents = WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT
- | WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
- settings.numBssidsPerScan = 0;
- settings.periodInMs = PERIODIC_SCAN_INTERVAL_MS;
-
- mPeriodicScanListener.clearScanDetails();
- mScanner.startBackgroundScan(settings, mPeriodicScanListener, WIFI_WORK_SOURCE);
+ if (scanImmediately) {
+ resetLastPeriodicSingleScanTimeStamp();
}
+ mPeriodicSingleScanInterval = PERIODIC_SCAN_INTERVAL_MS;
+ startPeriodicSingleScan();
}
// Start a DisconnectedPNO scan when screen is off and Wifi is disconnected
private void startDisconnectedPnoScan() {
+ // TODO(b/29503772): Need to change this interface.
+
// Initialize PNO settings
PnoSettings pnoSettings = new PnoSettings();
- ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =
- mConfigManager.retrieveDisconnectedPnoNetworkList();
+ List<PnoSettings.PnoNetwork> pnoNetworkList = mConfigManager.retrievePnoNetworkList();
int listSize = pnoNetworkList.size();
if (listSize == 0) {
@@ -873,8 +908,6 @@
scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
scanSettings.numBssidsPerScan = 0;
scanSettings.periodInMs = DISCONNECTED_PNO_SCAN_INTERVAL_MS;
- // TODO: enable exponential back off scan later to further save energy
- // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;
mPnoScanListener.clearScanDetails();
@@ -882,51 +915,7 @@
mPnoScanStarted = true;
}
- // Start a ConnectedPNO scan when screen is off and Wifi is connected
- private void startConnectedPnoScan() {
- // Disable ConnectedPNO for now due to b/28020168
- if (!ENABLE_CONNECTED_PNO_SCAN) {
- return;
- }
-
- // Initialize PNO settings
- PnoSettings pnoSettings = new PnoSettings();
- ArrayList<PnoSettings.PnoNetwork> pnoNetworkList =
- mConfigManager.retrieveConnectedPnoNetworkList();
- int listSize = pnoNetworkList.size();
-
- if (listSize == 0) {
- // No saved network
- localLog("No saved network for starting connected PNO.");
- return;
- }
-
- pnoSettings.networkList = new PnoSettings.PnoNetwork[listSize];
- pnoSettings.networkList = pnoNetworkList.toArray(pnoSettings.networkList);
- pnoSettings.min5GHzRssi = mMin5GHzRssi;
- pnoSettings.min24GHzRssi = mMin24GHzRssi;
- pnoSettings.initialScoreMax = mInitialScoreMax;
- pnoSettings.currentConnectionBonus = mCurrentConnectionBonus;
- pnoSettings.sameNetworkBonus = mSameNetworkBonus;
- pnoSettings.secureBonus = mSecureBonus;
- pnoSettings.band5GHzBonus = mBand5GHzBonus;
-
- // Initialize scan settings
- ScanSettings scanSettings = new ScanSettings();
- scanSettings.band = getScanBand();
- scanSettings.reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH;
- scanSettings.numBssidsPerScan = 0;
- scanSettings.periodInMs = CONNECTED_PNO_SCAN_INTERVAL_MS;
- // TODO: enable exponential back off scan later to further save energy
- // scanSettings.maxPeriodInMs = 8 * scanSettings.periodInMs;
-
- mPnoScanListener.clearScanDetails();
-
- mScanner.startConnectedPnoScan(scanSettings, pnoSettings, mPnoScanListener);
- mPnoScanStarted = true;
- }
-
- // Stop a PNO scan. This includes both DisconnectedPNO and ConnectedPNO scans.
+ // Stop PNO scan.
private void stopPnoScan() {
if (mPnoScanStarted) {
mScanner.stopPnoScan(mPnoScanListener);
@@ -937,10 +926,10 @@
// Set up watchdog timer
private void scheduleWatchdogTimer() {
- Log.i(TAG, "scheduleWatchdogTimer");
+ localLog("scheduleWatchdogTimer");
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + WATCHDOG_INTERVAL_MS,
+ mClock.getElapsedSinceBootMillis() + WATCHDOG_INTERVAL_MS,
WATCHDOG_TIMER_TAG,
mWatchdogListener, mEventHandler);
}
@@ -948,7 +937,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;
@@ -969,7 +958,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);
}
@@ -979,7 +968,7 @@
localLog("scheduleDelayedConnectivityScan");
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + msFromNow,
+ mClock.getElapsedSinceBootMillis() + msFromNow,
RESTART_CONNECTIVITY_SCAN_TIMER_TAG,
mRestartScanListener, mEventHandler);
@@ -989,11 +978,11 @@
// the current screen state and WiFi state.
private void startConnectivityScan(boolean scanImmediately) {
localLog("startConnectivityScan: screenOn=" + mScreenOn
- + " wifiState=" + mWifiState
- + " scanImmediately=" + scanImmediately
- + " wifiEnabled=" + mWifiEnabled
- + " wifiConnectivityManagerEnabled="
- + mWifiConnectivityManagerEnabled);
+ + " wifiState=" + stateToString(mWifiState)
+ + " scanImmediately=" + scanImmediately
+ + " wifiEnabled=" + mWifiEnabled
+ + " wifiConnectivityManagerEnabled="
+ + mWifiConnectivityManagerEnabled);
if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) {
return;
@@ -1010,24 +999,19 @@
if (mScreenOn) {
startPeriodicScan(scanImmediately);
- } else { // screenOff
- if (mWifiState == WIFI_STATE_CONNECTED) {
- startConnectedPnoScan();
- } else {
+ } else {
+ if (mWifiState == WIFI_STATE_DISCONNECTED && !mPnoScanStarted) {
startDisconnectedPnoScan();
}
}
+
}
// Stop connectivity scan if there is any.
private void stopConnectivityScan() {
// Due to b/28020168, timer based single scan will be scheduled
// to provide periodic scan in an exponential backoff fashion.
- if (!ENABLE_BACKGROUND_SCAN) {
- cancelPeriodicScanTimer();
- } else {
- mScanner.stopBackgroundScan(mPeriodicScanListener);
- }
+ cancelPeriodicScanTimer();
stopPnoScan();
mScanRestartCount = 0;
}
@@ -1044,10 +1028,26 @@
}
/**
+ * Helper function that converts the WIFI_STATE_XXX constants to string
+ */
+ private static String stateToString(int state) {
+ switch (state) {
+ case WIFI_STATE_CONNECTED:
+ return "connected";
+ case WIFI_STATE_DISCONNECTED:
+ return "disconnected";
+ case WIFI_STATE_TRANSITIONING:
+ return "transitioning";
+ default:
+ return "unknown";
+ }
+ }
+
+ /**
* Handler for WiFi state (connected/disconnected) changes
*/
public void handleConnectionStateChanged(int state) {
- localLog("handleConnectionStateChanged: state=" + state);
+ localLog("handleConnectionStateChanged: state=" + stateToString(state));
mWifiState = state;
@@ -1056,16 +1056,17 @@
if (mWifiState == WIFI_STATE_DISCONNECTED) {
mLastConnectionAttemptBssid = null;
scheduleWatchdogTimer();
+ startConnectivityScan(SCAN_IMMEDIATELY);
+ } else {
+ startConnectivityScan(SCAN_ON_SCHEDULE);
}
-
- startConnectivityScan(SCAN_ON_SCHEDULE);
}
/**
* Handler when user toggles whether untrusted connection is allowed
*/
public void setUntrustedConnectionAllowed(boolean allowed) {
- Log.i(TAG, "setUntrustedConnectionAllowed: allowed=" + allowed);
+ localLog("setUntrustedConnectionAllowed: allowed=" + allowed);
if (mUntrustedConnectionAllowed != allowed) {
mUntrustedConnectionAllowed = allowed;
@@ -1076,107 +1077,252 @@
/**
* Handler when user specifies a particular network to connect to
*/
- public void connectToUserSelectNetwork(int netId, boolean persistent) {
- Log.i(TAG, "connectToUserSelectNetwork: netId=" + netId
- + " persist=" + persistent);
+ public void setUserConnectChoice(int netId) {
+ localLog("setUserConnectChoice: netId=" + netId);
- mQualifiedNetworkSelector.userSelectNetwork(netId, persistent);
+ mNetworkSelector.setUserConnectChoice(netId);
+ }
+
+ /**
+ * Handler to prepare for connection to a user or app specified network
+ */
+ public void prepareForForcedConnection(int netId) {
+ localLog("prepareForForcedConnection: netId=" + netId);
clearConnectionAttemptTimeStamps();
+ clearBssidBlacklist();
}
/**
* Handler for on-demand connectivity scan
*/
public void forceConnectivityScan() {
- Log.i(TAG, "forceConnectivityScan");
+ localLog("forceConnectivityScan");
mWaitForFullBandScanResults = true;
startSingleScan(true);
}
/**
- * Track whether a BSSID should be enabled or disabled for QNS
+ * Update the BSSID blacklist when a BSSID is enabled or disabled
+ *
+ * @param bssid the bssid to be enabled/disabled
+ * @param enable -- true enable the bssid
+ * -- false disable the bssid
+ * @param reasonCode enable/disable reason code
+ * @return true if blacklist is updated; false otherwise
*/
- public boolean trackBssid(String bssid, boolean enable) {
- Log.i(TAG, "trackBssid: " + (enable ? "enable " : "disable ") + bssid);
-
- boolean ret = mQualifiedNetworkSelector
- .enableBssidForQualityNetworkSelection(bssid, enable);
-
- if (ret && !enable) {
- // Disabling a BSSID can happen when the AP candidate to connect to has
- // no capacity for new stations. We start another scan immediately so that QNS
- // can give us another candidate to connect to.
- startConnectivityScan(SCAN_IMMEDIATELY);
+ private boolean updateBssidBlacklist(String bssid, boolean enable, int reasonCode) {
+ // Remove the bssid from blacklist when it is enabled.
+ if (enable) {
+ return mBssidBlacklist.remove(bssid) != null;
}
- return ret;
+ // Update the bssid's blacklist status when it is disabled because of
+ // association rejection.
+ BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
+ if (status == null) {
+ // First time for this BSSID
+ status = new BssidBlacklistStatus();
+ mBssidBlacklist.put(bssid, status);
+ }
+
+ status.blacklistedTimeStamp = mClock.getElapsedSinceBootMillis();
+ status.counter++;
+ if (!status.isBlacklisted) {
+ if (status.counter >= BSSID_BLACKLIST_THRESHOLD
+ || reasonCode == REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA) {
+ status.isBlacklisted = true;
+ return true;
+ }
+ }
+ return false;
}
/**
- * Set band preference when doing scan and making connection
+ * Track whether a BSSID should be enabled or disabled for WifiNetworkSelector
+ *
+ * @param bssid the bssid to be enabled/disabled
+ * @param enable -- true enable the bssid
+ * -- false disable the bssid
+ * @param reasonCode enable/disable reason code
+ * @return true if blacklist is updated; false otherwise
*/
- public void setUserPreferredBand(int band) {
- Log.i(TAG, "User band preference: " + band);
+ public boolean trackBssid(String bssid, boolean enable, int reasonCode) {
+ localLog("trackBssid: " + (enable ? "enable " : "disable ") + bssid + " reason code "
+ + reasonCode);
- mQualifiedNetworkSelector.setUserPreferredBand(band);
+ if (bssid == null) {
+ return false;
+ }
+
+ if (!updateBssidBlacklist(bssid, enable, reasonCode)) {
+ return false;
+ }
+
+ // Blacklist was updated, so update firmware roaming configuration.
+ updateFirmwareRoamingConfiguration();
+
+ if (!enable) {
+ // Disabling a BSSID can happen when connection to the AP was rejected.
+ // We start another scan immediately so that WifiNetworkSelector can
+ // give us another candidate to connect to.
+ startConnectivityScan(SCAN_IMMEDIATELY);
+ }
+
+ return true;
+ }
+
+ /**
+ * Check whether a bssid is disabled
+ */
+ @VisibleForTesting
+ public boolean isBssidDisabled(String bssid) {
+ BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
+ return status == null ? false : status.isBlacklisted;
+ }
+
+ /**
+ * Compile and return a hashset of the blacklisted BSSIDs
+ */
+ private HashSet<String> buildBssidBlacklist() {
+ HashSet<String> blacklistedBssids = new HashSet<String>();
+ for (String bssid : mBssidBlacklist.keySet()) {
+ if (isBssidDisabled(bssid)) {
+ blacklistedBssids.add(bssid);
+ }
+ }
+
+ return blacklistedBssids;
+ }
+
+ /**
+ * Update firmware roaming configuration if the firmware roaming feature is supported.
+ * Compile and write the BSSID blacklist only. TODO(b/36488259): SSID whitelist is always
+ * empty for now.
+ */
+ private void updateFirmwareRoamingConfiguration() {
+ if (!mConnectivityHelper.isFirmwareRoamingSupported()) {
+ return;
+ }
+
+ int maxBlacklistSize = mConnectivityHelper.getMaxNumBlacklistBssid();
+ if (maxBlacklistSize <= 0) {
+ Log.wtf(TAG, "Invalid max BSSID blacklist size: " + maxBlacklistSize);
+ return;
+ }
+
+ ArrayList<String> blacklistedBssids = new ArrayList<String>(buildBssidBlacklist());
+ int blacklistSize = blacklistedBssids.size();
+
+ if (blacklistSize > maxBlacklistSize) {
+ Log.wtf(TAG, "Attempt to write " + blacklistSize + " blacklisted BSSIDs, max size is "
+ + maxBlacklistSize);
+
+ blacklistedBssids = new ArrayList<String>(blacklistedBssids.subList(0,
+ maxBlacklistSize));
+ localLog("Trim down BSSID blacklist size from " + blacklistSize + " to "
+ + blacklistedBssids.size());
+ }
+
+ if (!mConnectivityHelper.setFirmwareRoamingConfiguration(blacklistedBssids,
+ new ArrayList<String>())) { // TODO(b/36488259): SSID whitelist management.
+ localLog("Failed to set firmware roaming configuration.");
+ }
+ }
+
+ /**
+ * Refresh the BSSID blacklist
+ *
+ * Go through the BSSID blacklist and check if a BSSID has been blacklisted for
+ * BSSID_BLACKLIST_EXPIRE_TIME_MS. If yes, re-enable it.
+ */
+ private void refreshBssidBlacklist() {
+ if (mBssidBlacklist.isEmpty()) {
+ return;
+ }
+
+ boolean updated = false;
+ Iterator<BssidBlacklistStatus> iter = mBssidBlacklist.values().iterator();
+ Long currentTimeStamp = mClock.getElapsedSinceBootMillis();
+
+ while (iter.hasNext()) {
+ BssidBlacklistStatus status = iter.next();
+ if (status.isBlacklisted && ((currentTimeStamp - status.blacklistedTimeStamp)
+ >= BSSID_BLACKLIST_EXPIRE_TIME_MS)) {
+ iter.remove();
+ updated = true;
+ }
+ }
+
+ if (updated) {
+ updateFirmwareRoamingConfiguration();
+ }
+ }
+
+ /**
+ * Clear the BSSID blacklist
+ */
+ private void clearBssidBlacklist() {
+ mBssidBlacklist.clear();
+ updateFirmwareRoamingConfiguration();
+ }
+
+ /**
+ * Start WifiConnectivityManager
+ */
+ private void start() {
+ mConnectivityHelper.getFirmwareRoamingInfo();
+ clearBssidBlacklist();
startConnectivityScan(SCAN_IMMEDIATELY);
}
/**
+ * Stop and reset WifiConnectivityManager
+ */
+ private void stop() {
+ stopConnectivityScan();
+ clearBssidBlacklist();
+ resetLastPeriodicSingleScanTimeStamp();
+ mLastConnectionAttemptBssid = null;
+ mWaitForFullBandScanResults = false;
+ }
+
+ /**
+ * Update WifiConnectivityManager running state
+ *
+ * Start WifiConnectivityManager only if both Wifi and WifiConnectivityManager
+ * are enabled, otherwise stop it.
+ */
+ private void updateRunningState() {
+ if (mWifiEnabled && mWifiConnectivityManagerEnabled) {
+ localLog("Starting up WifiConnectivityManager");
+ start();
+ } else {
+ localLog("Stopping WifiConnectivityManager");
+ stop();
+ }
+ }
+
+ /**
* Inform WiFi is enabled for connection or not
*/
public void setWifiEnabled(boolean enable) {
- Log.i(TAG, "Set WiFi " + (enable ? "enabled" : "disabled"));
+ localLog("Set WiFi " + (enable ? "enabled" : "disabled"));
mWifiEnabled = enable;
+ updateRunningState();
- if (!mWifiEnabled) {
- stopConnectivityScan();
- resetLastPeriodicSingleScanTimeStamp();
- mLastConnectionAttemptBssid = null;
- mWaitForFullBandScanResults = false;
- } else if (mWifiConnectivityManagerEnabled) {
- startConnectivityScan(SCAN_IMMEDIATELY);
- }
}
/**
- * Turn on/off the WifiConnectivityMangager at runtime
+ * Turn on/off the WifiConnectivityManager at runtime
*/
public void enable(boolean enable) {
- Log.i(TAG, "Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled"));
+ localLog("Set WiFiConnectivityManager " + (enable ? "enabled" : "disabled"));
mWifiConnectivityManagerEnabled = enable;
-
- if (!mWifiConnectivityManagerEnabled) {
- stopConnectivityScan();
- resetLastPeriodicSingleScanTimeStamp();
- mLastConnectionAttemptBssid = null;
- mWaitForFullBandScanResults = false;
- } else if (mWifiEnabled) {
- startConnectivityScan(SCAN_IMMEDIATELY);
- }
- }
-
- /**
- * Enable/disable verbose logging
- */
- public void enableVerboseLogging(int verbose) {
- mDbg = verbose > 0;
- }
-
- /**
- * Dump the local log buffer
- */
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("Dump of WifiConnectivityManager");
- pw.println("WifiConnectivityManager - Log Begin ----");
- pw.println("WifiConnectivityManager - Number of connectivity attempts rate limited: "
- + mTotalConnectivityAttemptsRateLimited);
- mLocalLog.dump(fd, pw, args);
- pw.println("WifiConnectivityManager - Log End ----");
+ updateRunningState();
}
@VisibleForTesting
@@ -1188,4 +1334,14 @@
long getLastPeriodicSingleScanTimeStamp() {
return mLastPeriodicSingleScanTimeStamp;
}
+
+ /**
+ * Dump the local logs.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Dump of WifiConnectivityManager");
+ pw.println("WifiConnectivityManager - Log Begin ----");
+ mLocalLog.dump(fd, pw, args);
+ pw.println("WifiConnectivityManager - Log End ----");
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiController.java b/service/java/com/android/server/wifi/WifiController.java
index 67a7a42..c1b1861 100644
--- a/service/java/com/android/server/wifi/WifiController.java
+++ b/service/java/com/android/server/wifi/WifiController.java
@@ -30,7 +30,6 @@
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Looper;
@@ -270,13 +269,13 @@
}
};
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.STAY_ON_WHILE_PLUGGED_IN),
- false, contentObserver);
+ mFacade.registerContentObserver(mContext,
+ Settings.Global.getUriFor(Settings.Global.STAY_ON_WHILE_PLUGGED_IN), false,
+ contentObserver);
}
/**
- * Observes settings changes to scan always mode.
+ * Observes settings changes to wifi idle time.
*/
private void registerForWifiIdleTimeChange(Handler handler) {
ContentObserver contentObserver = new ContentObserver(handler) {
@@ -286,9 +285,8 @@
}
};
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.WIFI_IDLE_MS),
- false, contentObserver);
+ mFacade.registerContentObserver(mContext,
+ Settings.Global.getUriFor(Settings.Global.WIFI_IDLE_MS), false, contentObserver);
}
/**
@@ -301,9 +299,9 @@
readWifiSleepPolicy();
}
};
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.WIFI_SLEEP_POLICY),
- false, contentObserver);
+ mFacade.registerContentObserver(mContext,
+ Settings.Global.getUriFor(Settings.Global.WIFI_SLEEP_POLICY), false,
+ contentObserver);
}
/**
@@ -458,6 +456,11 @@
break;
}
if (mDeviceIdle == false) {
+ // wifi is toggled, we need to explicitly tell WifiStateMachine that we
+ // are headed to connect mode before going to the DeviceActiveState
+ // since that will start supplicant and WifiStateMachine may not know
+ // what state to head to (it might go to scan mode).
+ mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
transitionTo(mDeviceActiveState);
} else {
checkLocksAndTransitionWhenDeviceIdle();
@@ -476,7 +479,7 @@
if (msg.arg2 == 0) { // previous wifi state has not been saved yet
mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED);
}
- mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj,
+ mWifiStateMachine.setHostApRunning((SoftApModeConfiguration) msg.obj,
true);
transitionTo(mApEnabledState);
}
@@ -585,7 +588,6 @@
// in to client mode
mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
mWifiStateMachine.setSupplicantRunning(true);
- mWifiStateMachine.setDriverStart(true);
// Supplicant can't restart right away, so not the time we switched off
mDisabledTimestamp = SystemClock.elapsedRealtime();
mDeferredEnableSerialNumber++;
@@ -817,7 +819,6 @@
@Override
public void enter() {
mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mWifiStateMachine.setDriverStart(true);
mWifiStateMachine.setHighPerfModeEnabled(false);
}
@@ -868,7 +869,6 @@
@Override
public void enter() {
mWifiStateMachine.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
- mWifiStateMachine.setDriverStart(true);
}
}
@@ -877,7 +877,6 @@
@Override
public void enter() {
mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mWifiStateMachine.setDriverStart(true);
mWifiStateMachine.setHighPerfModeEnabled(false);
}
}
@@ -887,7 +886,6 @@
@Override
public void enter() {
mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mWifiStateMachine.setDriverStart(true);
mWifiStateMachine.setHighPerfModeEnabled(true);
}
}
@@ -896,7 +894,7 @@
class NoLockHeldState extends State {
@Override
public void enter() {
- mWifiStateMachine.setDriverStart(false);
+ mWifiStateMachine.setOperationalMode(WifiStateMachine.DISABLED_MODE);
}
}
diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java
index 1339656..e69fb8e 100644
--- a/service/java/com/android/server/wifi/WifiCountryCode.java
+++ b/service/java/com/android/server/wifi/WifiCountryCode.java
@@ -42,15 +42,12 @@
public WifiCountryCode(
WifiNative wifiNative,
String oemDefaultCountryCode,
- String persistentCountryCode,
boolean revertCountryCodeOnCellularLoss) {
mWifiNative = wifiNative;
mRevertCountryCodeOnCellularLoss = revertCountryCodeOnCellularLoss;
- if (!TextUtils.isEmpty(persistentCountryCode)) {
- mDefaultCountryCode = persistentCountryCode.toUpperCase();
- } else if (!TextUtils.isEmpty(oemDefaultCountryCode)) {
+ if (!TextUtils.isEmpty(oemDefaultCountryCode)) {
mDefaultCountryCode = oemDefaultCountryCode.toUpperCase();
} else {
if (mRevertCountryCodeOnCellularLoss) {
@@ -132,17 +129,15 @@
* otherwise we think it is from other applications.
* @return Returns true if the country code passed in is acceptable.
*/
- public synchronized boolean setCountryCode(String countryCode, boolean persist) {
+ public synchronized boolean setCountryCode(String countryCode) {
if (DBG) Log.d(TAG, "Receive set country code request: " + countryCode);
- // Ignore empty country code.
+ // Empty country code.
if (TextUtils.isEmpty(countryCode)) {
- if (DBG) Log.d(TAG, "Ignore empty country code");
- return false;
+ if (DBG) Log.d(TAG, "Received empty country code, reset to default country code");
+ mTelephonyCountryCode = null;
+ } else {
+ mTelephonyCountryCode = countryCode.toUpperCase();
}
- if (persist) {
- mDefaultCountryCode = countryCode;
- }
- mTelephonyCountryCode = countryCode.toUpperCase();
// If wpa_supplicant is ready we set the country code now, otherwise it will be
// set once wpa_supplicant is ready.
if (mReady) {
diff --git a/service/java/com/android/server/wifi/WifiLogger.java b/service/java/com/android/server/wifi/WifiDiagnostics.java
similarity index 89%
rename from service/java/com/android/server/wifi/WifiLogger.java
rename to service/java/com/android/server/wifi/WifiDiagnostics.java
index c15e2a8..b2acb2a 100644
--- a/service/java/com/android/server/wifi/WifiLogger.java
+++ b/service/java/com/android/server/wifi/WifiDiagnostics.java
@@ -18,10 +18,9 @@
import android.content.Context;
import android.util.Base64;
-import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.util.ByteArrayRingBuffer;
import com.android.server.wifi.util.StringUtil;
@@ -31,7 +30,6 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
-import java.lang.StringBuilder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Calendar;
@@ -43,15 +41,15 @@
/**
* Tracks various logs for framework.
*/
-class WifiLogger extends BaseWifiLogger {
+class WifiDiagnostics extends BaseWifiDiagnostics {
/**
* Thread-safety:
* 1) All non-private methods are |synchronized|.
- * 2) Callbacks into WifiLogger use non-private (and hence, synchronized) methods. See, e.g,
+ * 2) Callbacks into WifiDiagnostics use non-private (and hence, synchronized) methods. See, e.g,
* onRingBufferData(), onWifiAlert().
*/
- private static final String TAG = "WifiLogger";
+ private static final String TAG = "WifiDiags";
private static final boolean DBG = false;
/** log level flags; keep these consistent with wifi_logger.h */
@@ -103,22 +101,28 @@
private WifiNative.RingBufferStatus[] mRingBuffers;
private WifiNative.RingBufferStatus mPerPacketRingBuffer;
private WifiStateMachine mWifiStateMachine;
- private final WifiNative mWifiNative;
private final BuildProperties mBuildProperties;
+ private final WifiLog mLog;
+ private final LastMileLogger mLastMileLogger;
+ private final Runtime mJavaRuntime;
private int mMaxRingBufferSizeBytes;
- public WifiLogger(Context context, WifiStateMachine wifiStateMachine, WifiNative wifiNative,
- BuildProperties buildProperties) {
+ public WifiDiagnostics(Context context, WifiInjector wifiInjector,
+ WifiStateMachine wifiStateMachine, WifiNative wifiNative,
+ BuildProperties buildProperties, LastMileLogger lastMileLogger) {
+ super(wifiNative);
RING_BUFFER_BYTE_LIMIT_SMALL = context.getResources().getInteger(
R.integer.config_wifi_logger_ring_buffer_default_size_limit_kb) * 1024;
RING_BUFFER_BYTE_LIMIT_LARGE = context.getResources().getInteger(
R.integer.config_wifi_logger_ring_buffer_verbose_size_limit_kb) * 1024;
mWifiStateMachine = wifiStateMachine;
- mWifiNative = wifiNative;
mBuildProperties = buildProperties;
mIsLoggingEventHandlerRegistered = false;
mMaxRingBufferSizeBytes = RING_BUFFER_BYTE_LIMIT_SMALL;
+ mLog = wifiInjector.makeLog(TAG);
+ mLastMileLogger = lastMileLogger;
+ mJavaRuntime = wifiInjector.getJavaRuntime();
}
@Override
@@ -153,7 +157,7 @@
}
if (!mWifiNative.startPktFateMonitoring()) {
- Log.e(TAG, "Failed to start packet fate monitoring");
+ mLog.wC("Failed to start packet fate monitoring");
}
}
@@ -162,7 +166,7 @@
if (mPerPacketRingBuffer != null) {
startLoggingRingBuffer(mPerPacketRingBuffer);
} else {
- if (DBG) Log.d(TAG, "There is no per packet ring buffer");
+ if (DBG) mLog.tC("There is no per packet ring buffer");
}
}
@@ -171,7 +175,7 @@
if (mPerPacketRingBuffer != null) {
stopLoggingRingBuffer(mPerPacketRingBuffer);
} else {
- if (DBG) Log.d(TAG, "There is no per packet ring buffer");
+ if (DBG) mLog.tC("There is no per packet ring buffer");
}
}
@@ -179,9 +183,9 @@
public synchronized void stopLogging() {
if (mIsLoggingEventHandlerRegistered) {
if (!mWifiNative.resetLogHandler()) {
- Log.e(TAG, "Fail to reset log handler");
+ mLog.wC("Fail to reset log handler");
} else {
- if (DBG) Log.d(TAG, "Reset log handler");
+ if (DBG) mLog.tC("Reset log handler");
}
// Clear mIsLoggingEventHandlerRegistered even if resetLogHandler() failed, because
// the log handler is in an indeterminate state.
@@ -195,8 +199,11 @@
}
@Override
- synchronized void reportConnectionFailure() {
- mPacketFatesForLastFailure = fetchPacketFates();
+ synchronized void reportConnectionEvent(long connectionId, byte event) {
+ mLastMileLogger.reportConnectionEvent(connectionId, event);
+ if (event == CONNECTION_EVENT_FAILED) {
+ mPacketFatesForLastFailure = fetchPacketFates();
+ }
}
@Override
@@ -231,11 +238,9 @@
}
dumpPacketFates(pw);
- pw.println("--------------------------------------------------------------------");
+ mLastMileLogger.dump(pw);
- pw.println("WifiNative - Log Begin ----");
- mWifiNative.getLocalLog().dump(fd, pw, args);
- pw.println("WifiNative - Log End ----");
+ pw.println("--------------------------------------------------------------------");
}
/* private methods and data */
@@ -368,12 +373,12 @@
new WifiNative.WifiLoggerEventHandler() {
@Override
public void onRingBufferData(WifiNative.RingBufferStatus status, byte[] buffer) {
- WifiLogger.this.onRingBufferData(status, buffer);
+ WifiDiagnostics.this.onRingBufferData(status, buffer);
}
@Override
public void onWifiAlert(int errorCode, byte[] buffer) {
- WifiLogger.this.onWifiAlert(errorCode, buffer);
+ WifiDiagnostics.this.onWifiAlert(errorCode, buffer);
}
};
@@ -408,12 +413,17 @@
}
private boolean fetchRingBuffers() {
+ if (mBuildProperties.isUserBuild()) {
+ mRingBuffers = null;
+ return false;
+ }
+
if (mRingBuffers != null) return true;
mRingBuffers = mWifiNative.getRingBufferStatus();
if (mRingBuffers != null) {
for (WifiNative.RingBufferStatus buffer : mRingBuffers) {
- if (DBG) Log.d(TAG, "RingBufferStatus is: \n" + buffer.name);
+ if (DBG) mLog.trace("RingBufferStatus is: %").c(buffer.name).flush();
if (mRingBufferData.containsKey(buffer.name) == false) {
mRingBufferData.put(buffer.name,
new ByteArrayRingBuffer(mMaxRingBufferSizeBytes));
@@ -423,7 +433,7 @@
}
}
} else {
- Log.e(TAG, "no ring buffers found");
+ mLog.wC("no ring buffers found");
}
return mRingBuffers != null;
@@ -438,7 +448,7 @@
private boolean startLoggingAllExceptPerPacketBuffers() {
if (mRingBuffers == null) {
- if (DBG) Log.d(TAG, "No ring buffers to log anything!");
+ if (DBG) mLog.tC("No ring buffers to log anything!");
return false;
}
@@ -446,7 +456,7 @@
if ((buffer.flag & RING_BUFFER_FLAG_HAS_PER_PACKET_ENTRIES) != 0) {
/* skip per-packet-buffer */
- if (DBG) Log.d(TAG, "skipped per packet logging ring " + buffer.name);
+ if (DBG) mLog.trace("skipped per packet logging ring %").c(buffer.name).flush();
continue;
}
@@ -463,7 +473,7 @@
if (mWifiNative.startLoggingRingBuffer(
mLogLevel, 0, minInterval, minDataSize, buffer.name) == false) {
- if (DBG) Log.e(TAG, "Could not start logging ring " + buffer.name);
+ if (DBG) mLog.warn("Could not start logging ring %").c(buffer.name).flush();
return false;
}
@@ -472,7 +482,7 @@
private boolean stopLoggingRingBuffer(WifiNative.RingBufferStatus buffer) {
if (mWifiNative.startLoggingRingBuffer(0, 0, 0, 0, buffer.name) == false) {
- if (DBG) Log.e(TAG, "Could not stop logging ring " + buffer.name);
+ if (DBG) mLog.warn("Could not stop logging ring %").c(buffer.name).flush();
}
return true;
}
@@ -486,24 +496,6 @@
return true;
}
- private boolean getAllRingBufferData() {
- if (mRingBuffers == null) {
- Log.e(TAG, "Not ring buffers available to collect data!");
- return false;
- }
-
- for (WifiNative.RingBufferStatus element : mRingBuffers){
- boolean result = mWifiNative.getRingBufferData(element.name);
- if (!result) {
- Log.e(TAG, "Fail to get ring buffer data of: " + element.name);
- return false;
- }
- }
-
- Log.d(TAG, "getAllRingBufferData Successfully!");
- return true;
- }
-
private boolean enableVerboseLoggingForDogfood() {
return false;
}
@@ -542,7 +534,7 @@
return mLastBugReports;
}
- private static String compressToBase64(byte[] input) {
+ private String compressToBase64(byte[] input) {
String result;
//compress
Deflater compressor = new Deflater();
@@ -561,14 +553,14 @@
compressor.end();
bos.close();
} catch (IOException e) {
- Log.e(TAG, "ByteArrayOutputStream close error");
+ mLog.wC("ByteArrayOutputStream close error");
result = android.util.Base64.encodeToString(input, Base64.DEFAULT);
return result;
}
byte[] compressed = bos.toByteArray();
if (DBG) {
- Log.d(TAG," length is:" + (compressed == null? "0" : compressed.length));
+ mLog.dump("length is: %").c(compressed == null ? 0 : compressed.length).flush();
}
//encode
@@ -576,7 +568,7 @@
compressed.length < input.length ? compressed : input , Base64.DEFAULT);
if (DBG) {
- Log.d(TAG, "FwMemoryDump length is :" + result.length());
+ mLog.dump("FwMemoryDump length is: %").c(result.length()).flush();
}
return result;
@@ -585,7 +577,7 @@
private ArrayList<String> getLogcat(int maxLines) {
ArrayList<String> lines = new ArrayList<String>(maxLines);
try {
- Process process = Runtime.getRuntime().exec(String.format("logcat -t %d", maxLines));
+ Process process = mJavaRuntime.exec(String.format("logcat -t %d", maxLines));
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
@@ -599,20 +591,20 @@
}
process.waitFor();
} catch (InterruptedException|IOException e) {
- Log.e(TAG, "Exception while capturing logcat" + e);
+ mLog.dump("Exception while capturing logcat: %").c(e.toString()).flush();
}
return lines;
}
private LimitedCircularArray<String> getKernelLog(int maxLines) {
- if (DBG) Log.d(TAG, "Reading kernel log ...");
+ if (DBG) mLog.tC("Reading kernel log ...");
LimitedCircularArray<String> lines = new LimitedCircularArray<String>(maxLines);
String log = mWifiNative.readKernelLog();
String logLines[] = log.split("\n");
for (int i = 0; i < logLines.length; i++) {
lines.addLast(logLines[i]);
}
- if (DBG) Log.d(TAG, "Added " + logLines.length + " lines");
+ if (DBG) mLog.dump("Added % lines").c(logLines.length).flush();
return lines;
}
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 6939d45..c517785 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -16,34 +16,302 @@
package com.android.server.wifi;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IWifiScanner;
+import android.net.wifi.IWificond;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.WifiScanner;
+import android.os.BatteryStats;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.os.UserManager;
import android.security.KeyStore;
+import android.telephony.TelephonyManager;
+import android.util.LocalLog;
+
+import com.android.internal.R;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.am.BatteryStatsService;
+import com.android.server.net.DelayedDiskWrite;
+import com.android.server.net.IpConfigStore;
+import com.android.server.wifi.hotspot2.LegacyPasspointConfigParser;
+import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.hotspot2.PasspointNetworkEvaluator;
+import com.android.server.wifi.hotspot2.PasspointObjectFactory;
+import com.android.server.wifi.p2p.SupplicantP2pIfaceHal;
+import com.android.server.wifi.p2p.WifiP2pMonitor;
+import com.android.server.wifi.p2p.WifiP2pNative;
+import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
/**
- * 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 WifiApConfigStore mWifiApConfigStore;
+ private final WifiNative mWifiNative;
+ private final WifiMonitor mWifiMonitor;
+ private final WifiP2pNative mWifiP2pNative;
+ private final WifiP2pMonitor mWifiP2pMonitor;
+ private final SupplicantStaIfaceHal mSupplicantStaIfaceHal;
+ private final SupplicantP2pIfaceHal mSupplicantP2pIfaceHal;
+ private final WifiVendorHal mWifiVendorHal;
+ 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 WificondControl mWificondControl;
private final Clock mClock = new Clock();
- private final WifiMetrics mWifiMetrics = new WifiMetrics(mClock);
- private final WifiLastResortWatchdog mWifiLastResortWatchdog =
- new WifiLastResortWatchdog(mWifiMetrics);
+ private final WifiMetrics 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;
+ private final WifiMulticastLockManager mWifiMulticastLockManager;
+ private final WifiConfigStore mWifiConfigStore;
+ private final WifiKeyStore mWifiKeyStore;
+ private final WifiNetworkHistory mWifiNetworkHistory;
+ private final IpConfigStore mIpConfigStore;
+ private final WifiConfigStoreLegacy mWifiConfigStoreLegacy;
+ private final WifiConfigManager mWifiConfigManager;
+ private final WifiConnectivityHelper mWifiConnectivityHelper;
+ private final LocalLog mConnectivityLocalLog;
+ private final WifiNetworkSelector mWifiNetworkSelector;
+ private final SavedNetworkEvaluator mSavedNetworkEvaluator;
+ private final PasspointNetworkEvaluator mPasspointNetworkEvaluator;
+ private final ScoredNetworkEvaluator mScoredNetworkEvaluator;
+ private final WifiNetworkScoreCache mWifiNetworkScoreCache;
+ private final NetworkScoreManager mNetworkScoreManager;
+ private WifiScanner mWifiScanner;
+ private final WifiPermissionsWrapper mWifiPermissionsWrapper;
+ private final WifiPermissionsUtil mWifiPermissionsUtil;
+ private final PasspointManager mPasspointManager;
+ private final SIMAccessor mSimAccessor;
+ private HandlerThread mWifiAwareHandlerThread;
+ private HalDeviceManager mHalDeviceManager;
+ private final IBatteryStats mBatteryStats;
+ private final WifiStateTracker mWifiStateTracker;
+ private final Runtime mJavaRuntime;
+ private final SelfRecovery mSelfRecovery;
+
+ private final boolean mUseRealLogger;
+
+ 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;
+ mUseRealLogger = mContext.getResources().getBoolean(
+ R.bool.config_wifi_enable_wifi_firmware_debugging);
+ mSettingsStore = new WifiSettingsStore(mContext);
+ mWifiPermissionsWrapper = new WifiPermissionsWrapper(mContext);
+ mNetworkScoreManager = mContext.getSystemService(NetworkScoreManager.class);
+ mWifiNetworkScoreCache = new WifiNetworkScoreCache(mContext);
+ mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
+ mWifiNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_NONE);
+ mWifiPermissionsUtil = new WifiPermissionsUtil(mWifiPermissionsWrapper, mContext,
+ mSettingsStore, UserManager.get(mContext), mNetworkScoreManager, this);
+ mWifiBackupRestore = new WifiBackupRestore(mWifiPermissionsUtil);
+ mBatteryStats = IBatteryStats.Stub.asInterface(mFrameworkFacade.getService(
+ BatteryStats.SERVICE_NAME));
+ mWifiStateTracker = new WifiStateTracker(mBatteryStats);
+ // Now create and start handler threads
+ mWifiServiceHandlerThread = new HandlerThread("WifiService");
+ mWifiServiceHandlerThread.start();
+ mWifiStateMachineHandlerThread = new HandlerThread("WifiStateMachine");
+ mWifiStateMachineHandlerThread.start();
+ Looper wifiStateMachineLooper = mWifiStateMachineHandlerThread.getLooper();
+ mWifiMetrics = new WifiMetrics(mClock, wifiStateMachineLooper);
+ // Modules interacting with Native.
+ mWifiMonitor = new WifiMonitor(this);
+ mHalDeviceManager = new HalDeviceManager();
+ mWifiVendorHal =
+ new WifiVendorHal(mHalDeviceManager, mWifiStateMachineHandlerThread.getLooper());
+ mSupplicantStaIfaceHal = new SupplicantStaIfaceHal(mContext, mWifiMonitor);
+ mWificondControl = new WificondControl(this, mWifiMonitor);
+ mWifiNative = new WifiNative(SystemProperties.get("wifi.interface", "wlan0"),
+ mWifiVendorHal, mSupplicantStaIfaceHal, mWificondControl);
+ mWifiP2pMonitor = new WifiP2pMonitor(this);
+ mSupplicantP2pIfaceHal = new SupplicantP2pIfaceHal(mWifiP2pMonitor);
+ mWifiP2pNative = new WifiP2pNative(SystemProperties.get("wifi.direct.interface", "p2p0"),
+ mSupplicantP2pIfaceHal);
+
+ // Now get instances of all the objects that depend on the HandlerThreads
+ mTrafficPoller = new WifiTrafficPoller(mContext, mWifiServiceHandlerThread.getLooper(),
+ mWifiNative.getInterfaceName());
+ mCountryCode = new WifiCountryCode(mWifiNative,
+ SystemProperties.get(BOOT_DEFAULT_WIFI_COUNTRY_CODE),
+ mContext.getResources()
+ .getBoolean(R.bool.config_wifi_revert_country_code_on_cellular_loss));
+ mWifiApConfigStore = new WifiApConfigStore(mContext, mBackupManagerProxy);
+
+ // WifiConfigManager/Store objects and their dependencies.
+ // New config store
+ mWifiKeyStore = new WifiKeyStore(mKeyStore);
+ mWifiConfigStore = new WifiConfigStore(
+ mContext, wifiStateMachineLooper, mClock,
+ WifiConfigStore.createSharedFile());
+ // Legacy config store
+ DelayedDiskWrite writer = new DelayedDiskWrite();
+ mWifiNetworkHistory = new WifiNetworkHistory(mContext, writer);
+ mIpConfigStore = new IpConfigStore(writer);
+ mWifiConfigStoreLegacy = new WifiConfigStoreLegacy(
+ mWifiNetworkHistory, mWifiNative, mIpConfigStore,
+ new LegacyPasspointConfigParser());
+ // Config Manager
+ mWifiConfigManager = new WifiConfigManager(mContext, mClock,
+ UserManager.get(mContext), TelephonyManager.from(mContext),
+ mWifiKeyStore, mWifiConfigStore, mWifiConfigStoreLegacy, mWifiPermissionsUtil,
+ mWifiPermissionsWrapper, new NetworkListStoreData(),
+ new DeletedEphemeralSsidsStoreData());
+ mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative);
+ mConnectivityLocalLog = new LocalLog(ActivityManager.isLowRamDeviceStatic() ? 256 : 512);
+ mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiConfigManager, mClock,
+ mConnectivityLocalLog);
+ mSavedNetworkEvaluator = new SavedNetworkEvaluator(mContext,
+ mWifiConfigManager, mClock, mConnectivityLocalLog, mWifiConnectivityHelper);
+ mScoredNetworkEvaluator = new ScoredNetworkEvaluator(context, wifiStateMachineLooper,
+ mFrameworkFacade, mNetworkScoreManager, mWifiConfigManager, mConnectivityLocalLog,
+ mWifiNetworkScoreCache);
+ mSimAccessor = new SIMAccessor(mContext);
+ mPasspointManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock,
+ mSimAccessor, new PasspointObjectFactory(), mWifiConfigManager, mWifiConfigStore);
+ mPasspointNetworkEvaluator = new PasspointNetworkEvaluator(
+ mPasspointManager, mWifiConfigManager, mConnectivityLocalLog);
+ // mWifiStateMachine has an implicit dependency on mJavaRuntime due to WifiDiagnostics.
+ mJavaRuntime = Runtime.getRuntime();
+ mWifiStateMachine = new WifiStateMachine(mContext, mFrameworkFacade,
+ wifiStateMachineLooper, UserManager.get(mContext),
+ this, mBackupManagerProxy, mCountryCode, mWifiNative);
+ mCertManager = new WifiCertManager(mContext);
+ mNotificationController = new WifiNotificationController(mContext,
+ mWifiServiceHandlerThread.getLooper(), mFrameworkFacade, null, this);
+ mLockManager = new WifiLockManager(mContext, BatteryStatsService.getService());
+ mWifiController = new WifiController(mContext, mWifiStateMachine, mSettingsStore,
+ mLockManager, mWifiServiceHandlerThread.getLooper(), mFrameworkFacade);
+ mSelfRecovery = new SelfRecovery(mWifiController);
+ mWifiLastResortWatchdog = new WifiLastResortWatchdog(mSelfRecovery, 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 UserManager getUserManager() {
+ return UserManager.get(mContext);
+ }
public WifiMetrics getWifiMetrics() {
return mWifiMetrics;
}
+ public SupplicantStaIfaceHal getSupplicantStaIfaceHal() {
+ return mSupplicantStaIfaceHal;
+ }
+
+ 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 WifiApConfigStore getWifiApConfigStore() {
+ return mWifiApConfigStore;
+ }
+
+ 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 +324,163 @@
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 WifiConfigManager getWifiConfigManager() {
+ return mWifiConfigManager;
+ }
+
+ public PasspointManager getPasspointManager() {
+ return mPasspointManager;
+ }
+
+ public TelephonyManager makeTelephonyManager() {
+ // may not be available when WiFi starts
+ return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ public WifiStateTracker getWifiStateTracker() {
+ return mWifiStateTracker;
+ }
+
+ 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 nmService NetworkManagementService allowing SoftApManager to listen for interface
+ * changes
+ * @param listener listener for SoftApManager
+ * @param apInterface network interface to start hostapd against
+ * @param config softAp WifiConfiguration
+ * @return an instance of SoftApManager
+ */
+ public SoftApManager makeSoftApManager(INetworkManagementService nmService,
+ SoftApManager.Listener listener,
+ IApInterface apInterface,
+ WifiConfiguration config) {
+ return new SoftApManager(mWifiServiceHandlerThread.getLooper(),
+ mWifiNative, mCountryCode.getCountryCode(),
+ listener, apInterface, nmService,
+ mWifiApConfigStore, config, mWifiMetrics);
+ }
+
+ /**
+ * Create a WifiLog instance.
+ * @param tag module name to include in all log messages
+ */
+ public WifiLog makeLog(String tag) {
+ return new LogcatLog(tag);
+ }
+
+ public BaseWifiDiagnostics makeWifiDiagnostics(WifiNative wifiNative) {
+ if (mUseRealLogger) {
+ return new WifiDiagnostics(
+ mContext, this, mWifiStateMachine, wifiNative, mBuildProperties,
+ new LastMileLogger(this));
+ } else {
+ return new BaseWifiDiagnostics(wifiNative);
+ }
+ }
+
+ /**
+ * Obtain an instance of WifiScanner.
+ * If it was not already created, then obtain an instance. Note, this must be done lazily since
+ * WifiScannerService is separate and created later.
+ */
+ public synchronized WifiScanner getWifiScanner() {
+ if (mWifiScanner == null) {
+ mWifiScanner = new WifiScanner(mContext,
+ IWifiScanner.Stub.asInterface(ServiceManager.getService(
+ Context.WIFI_SCANNING_SERVICE)),
+ mWifiStateMachineHandlerThread.getLooper());
+ }
+ return mWifiScanner;
+ }
+
+ /**
+ * Obtain a new instance of WifiConnectivityManager.
+ *
+ * Create and return a new WifiConnectivityManager.
+ * @param wifiInfo WifiInfo object for updating wifi state.
+ * @param hasConnectionRequests boolean indicating if WifiConnectivityManager to start
+ * immediately based on connection requests.
+ */
+ public WifiConnectivityManager makeWifiConnectivityManager(WifiInfo wifiInfo,
+ boolean hasConnectionRequests) {
+ return new WifiConnectivityManager(mContext, mWifiStateMachine, getWifiScanner(),
+ mWifiConfigManager, wifiInfo, mWifiNetworkSelector, mWifiConnectivityHelper,
+ mWifiLastResortWatchdog, mWifiMetrics, mWifiStateMachineHandlerThread.getLooper(),
+ mClock, mConnectivityLocalLog, hasConnectionRequests, mFrameworkFacade,
+ mSavedNetworkEvaluator, mScoredNetworkEvaluator, mPasspointNetworkEvaluator);
+ }
+
+ public WifiPermissionsUtil getWifiPermissionsUtil() {
+ return mWifiPermissionsUtil;
+ }
+
+ public WifiPermissionsWrapper getWifiPermissionsWrapper() {
+ return mWifiPermissionsWrapper;
+ }
+
+ /**
+ * Returns a singleton instance of a HandlerThread for injection. Uses lazy initialization.
+ *
+ * TODO: share worker thread with other Wi-Fi handlers (b/27924886)
+ */
+ public HandlerThread getWifiAwareHandlerThread() {
+ if (mWifiAwareHandlerThread == null) { // lazy initialization
+ mWifiAwareHandlerThread = new HandlerThread("wifiAwareService");
+ mWifiAwareHandlerThread.start();
+ }
+ return mWifiAwareHandlerThread;
+ }
+
+ /**
+ * Returns a single instance of HalDeviceManager for injection.
+ */
+ public HalDeviceManager getHalDeviceManager() {
+ return mHalDeviceManager;
+ }
+
+ public Runtime getJavaRuntime() {
+ return mJavaRuntime;
+ }
+
+ public WifiNative getWifiNative() {
+ return mWifiNative;
+ }
+
+ public WifiMonitor getWifiMonitor() {
+ return mWifiMonitor;
+ }
+
+ public WifiP2pNative getWifiP2pNative() {
+ return mWifiP2pNative;
+ }
+
+ public WifiP2pMonitor getWifiP2pMonitor() {
+ return mWifiP2pMonitor;
+ }
+
+ public SelfRecovery getSelfRecovery() {
+ return mSelfRecovery;
+ }
}
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..e36c501
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiKeyStore.java
@@ -0,0 +1,293 @@
+/*
+ * 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;
+ Certificate[] clientCertificateChain = config.getClientCertificateChain();
+ if (clientCertificateChain != null && clientCertificateChain.length != 0) {
+ 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 = putCertsInKeyStore(userCertName, clientCertificateChain);
+ 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;
+ }
+
+ /**
+ * Install a certificate into the keystore.
+ *
+ * @param name The alias name of the certificate to be installed
+ * @param cert The certificate to be installed
+ * @return true on success
+ */
+ public boolean putCertInKeyStore(String name, Certificate cert) {
+ return putCertsInKeyStore(name, new Certificate[] {cert});
+ }
+
+ /**
+ * Install a client certificate chain into the keystore.
+ *
+ * @param name The alias name of the certificate to be installed
+ * @param certs The certificate chain to be installed
+ * @return true on success
+ */
+ public boolean putCertsInKeyStore(String name, Certificate[] certs) {
+ try {
+ byte[] certData = Credentials.convertToPem(certs);
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "putting " + certs.length + " certificate(s) "
+ + name + " in keystore");
+ }
+ return mKeyStore.put(name, certData, Process.WIFI_UID, KeyStore.FLAG_NONE);
+ } catch (IOException e1) {
+ return false;
+ } catch (CertificateException e2) {
+ return false;
+ }
+ }
+
+ /**
+ * Install a key into the keystore.
+ *
+ * @param name The alias name of the key to be installed
+ * @param key The key to be installed
+ * @return true on success
+ */
+ public boolean putKeyInKeyStore(String name, Key key) {
+ byte[] privKeyData = key.getEncoded();
+ return mKeyStore.importKey(name, privKeyData, Process.WIFI_UID, KeyStore.FLAG_NONE);
+ }
+
+ /**
+ * Remove a certificate or key entry specified by the alias name from the keystore.
+ *
+ * @param name The alias name of the entry to be removed
+ * @return true on success
+ */
+ public boolean removeEntryFromKeyStore(String name) {
+ return mKeyStore.delete(name, Process.WIFI_UID);
+ }
+
+ /**
+ * 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 0885e46..4cc8a20 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
*/
@@ -78,11 +77,11 @@
// successfully connecting or a new network (SSID) becomes available to connect to.
private boolean mWatchdogAllowedToTrigger = true;
+ private SelfRecovery mSelfRecovery;
private WifiMetrics mWifiMetrics;
- private WifiController mWifiController = null;
-
- WifiLastResortWatchdog(WifiMetrics wifiMetrics) {
+ WifiLastResortWatchdog(SelfRecovery selfRecovery, WifiMetrics wifiMetrics) {
+ mSelfRecovery = selfRecovery;
mWifiMetrics = wifiMetrics;
}
@@ -95,7 +94,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 +106,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 +164,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 +180,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,11 +189,14 @@
// 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);
- restartWifiStack();
+ Log.e(TAG, "Watchdog triggering recovery");
+ mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);
// increment various watchdog trigger count stats
incrementWifiMetricsTriggerCounts();
clearAllFailureCounts();
@@ -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 (!mWatchdogAllowedToTrigger) {
@@ -234,7 +239,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 + "]");
}
@@ -255,9 +260,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;
@@ -273,22 +276,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 + "]");
}
@@ -303,7 +302,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;
@@ -325,35 +324,17 @@
}
// 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;
}
/**
- * Trigger a restart of the wifi stack.
- */
- private void restartWifiStack() {
- if (VDBG) Log.v(TAG, "restartWifiStack.");
-
- // First verify that we can send the trigger message.
- if (mWifiController == null) {
- Log.e(TAG, "WifiLastResortWatchdog unable to trigger: WifiController is null");
- return;
- }
-
- if (DBG) Log.d(TAG, toString());
-
- mWifiController.sendMessage(WifiController.CMD_RESTART_WIFI);
- Log.i(TAG, "Triggered WiFi stack restart.");
- }
-
- /**
* 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());
@@ -385,7 +366,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();
@@ -409,7 +390,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;
}
@@ -423,14 +404,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();
@@ -463,9 +445,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;
@@ -481,6 +461,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).
@@ -543,24 +531,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/WifiLog.java b/service/java/com/android/server/wifi/WifiLog.java
new file mode 100644
index 0000000..24dcda6
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiLog.java
@@ -0,0 +1,250 @@
+/*
+ * 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.annotation.NonNull;
+
+import javax.annotation.CheckReturnValue;
+
+/**
+ * Provides an abstraction of logging back-ends.
+ *
+ * The abstraction is designed to
+ * a) minimize the cost of disabled log messages,
+ * b) allow callers to tag message parameters as containing sensitive
+ * information,
+ * c) avoid the use of format codes, and
+ * d) easily support additional data types.
+ *
+ * Implementations of WifiLog may or may not be thread-safe.
+ * Implementations of LogMessage are expected _not_ to be thread-safe,
+ * as LogMessage instances are not expected to be shared between threads.
+ */
+public interface WifiLog {
+ char PLACEHOLDER = '%';
+
+ // New-style API.
+ /**
+ * Allocate an error-level log message, which the caller will fill with
+ * additional parameters according to |format|. After filling the message
+ * with parameters, the caller must call flush(), to actually log the message.
+ *
+ * Error-level messages should be used when a malfunction has occurred,
+ * and the malfunction is likely to cause an externally visible problem.
+ * For example: we failed to initialize the Wifi interface.
+ *
+ * Typical usage is as follows:
+ * WifiDevice() {
+ * mLog = new LogcatLog("ModuleName");
+ * }
+ *
+ * void start() {
+ * // ...
+ * mLog.err("error % while starting interface %").c(errNum).c(ifaceName).flush();
+ * }
+ *
+ * void stop() {
+ * // ...
+ * mLog.err("error % while stopping interface %").c(errNum).c(ifaceName).flush();
+ * }
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage err(@NonNull String format);
+
+ /**
+ * Like {@link #err(String) err()}, except that a warning-level message is
+ * allocated.
+ *
+ * Warning-level messages should be used when a malfunction has occurred,
+ * but the malfunction is _unlikely_ to cause an externally visible problem
+ * on its own. For example: if we fail to start the debugging subsystem.
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage warn(@NonNull String format);
+
+ /**
+ * Like {@link #err(String) err()}, except that a info-level message is
+ * allocated.
+ *
+ * Info-level messages should be used to report progress or status messages
+ * that help understand the program's external behavior. For example: we
+ * might log an info message before initiating a Wifi association.
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage info(@NonNull String format);
+
+ /**
+ * Like {@link #err(String) err()}, except that a trace-level message is
+ * allocated.
+ *
+ * Trace-level messages should be used to report progress or status messages
+ * that help understand the program's internal behavior. For example:
+ * "Reached myCoolMethod()".
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage trace(@NonNull String format);
+
+ /**
+ * Like {@link #err(String) err()}, except that a dump-level message is
+ * allocated.
+ *
+ * Dump-level messages should be used to report detailed internal state.
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage dump(@NonNull String format);
+
+ /**
+ * Log a warning using the default tag for this WifiLog instance. Mark
+ * the message as 'clean' (i.e. _not_ containing any sensitive data).
+ *
+ * NOTE: this method should only be used for literal strings. For messages with
+ * parameters, use err().
+ *
+ * @param msg the message to be logged
+ */
+ void eC(String msg);
+
+ /**
+ * Like {@link #eC(String)} eC()}, except that a warning-level message
+ * is logged.
+ */
+ void wC(String msg);
+
+ /**
+ * Like {@link #eC(String)} eC()}, except that an info-level message
+ * is logged.
+ */
+ void iC(String msg);
+
+ /**
+ * Like {@link #eC(String)} eC()}, except that a trace-level message
+ * is logged.
+ */
+ void tC(String msg);
+
+ /**
+ * Note: dC() is deliberately omitted, as "dumping" is inherently at
+ * odds with the intention that the caller pass in a literal string.
+ */
+
+ /**
+ * Represents a single log message.
+ *
+ * Implementations are expected _not_ to be thread-safe.
+ */
+ interface LogMessage {
+ /**
+ * Replace the first available placeholder in this LogMessage's format
+ * with the specified value. Mark the value as 'raw', to inform the
+ * logging daemon that the value may contain sensitive data.
+ *
+ * @return |this|, to allow chaining of calls
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage r(String value);
+
+ /**
+ * Like {@link #r(String) r()}, except that the value is marked
+ * as 'clean', to inform the logging daemon that the value does _not_
+ * contain sensitive data.
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage c(String value);
+
+ /**
+ * Like {@link #c(String) c(String)}, except that the value is a long.
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage c(long value);
+
+ /**
+ * Like {@link #c(String) c(String)}, except that the value is a char.
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage c(char value);
+
+ /**
+ * Like {@link #c(String) c(String)}, except that the value is a boolean.
+ */
+ @CheckReturnValue
+ @NonNull
+ LogMessage c(boolean value);
+
+ /**
+ * Write this LogMessage to the logging daemon. Writing the
+ * message is best effort. More specifically:
+ * 1) The operation is non-blocking. If we’re unable to write
+ * the log message to the IPC channel, the message is
+ * dropped silently.
+ * 2) If the number of |value|s provided exceeds the number of
+ * placeholders in the |format|, then extraneous |value|s
+ * are silently dropped.
+ * 3) If the number of placeholders in the |format| exceeds
+ * the number of |value|s provided, the message is sent to
+ * the logging daemon without generating an Exception.
+ * 4) If the total message length exceeds the logging
+ * protocol’s maximum message length, the message is
+ * silently truncated.
+ */
+ void flush();
+ }
+
+ // Legacy API.
+ /**
+ * Log an error using the default tag for this WifiLog instance.
+ * @param msg the message to be logged
+ * TODO(b/30736737): Remove this method, once all code has migrated to alternatives.
+ */
+ void e(String msg);
+
+ /**
+ * Log a warning using the default tag for this WifiLog instance.
+ * @param msg the message to be logged
+ * TODO(b/30736737): Remove this method, once all code has migrated to alternatives.
+ */
+ void w(String msg);
+
+ /**
+ * Log an informational message using the default tag for this WifiLog instance.
+ * @param msg the message to be logged
+ * TODO(b/30736737): Remove this method, once all code has migrated to alternatives.
+ */
+ void i(String msg);
+
+ /**
+ * Log a debug message using the default tag for this WifiLog instance.
+ * @param msg the message to be logged
+ * TODO(b/30736737): Remove this method, once all code has migrated to alternatives.
+ */
+ void d(String msg);
+
+ /**
+ * Log a verbose message using the default tag for this WifiLog instance.
+ * @param msg the message to be logged
+ * TODO(b/30736737): Remove this method, once all code has migrated to alternatives.
+ */
+ void v(String msg);
+}
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java
index 6583db2..92a6fd6 100644
--- a/service/java/com/android/server/wifi/WifiMetrics.java
+++ b/service/java/com/android/server/wifi/WifiMetrics.java
@@ -16,20 +16,33 @@
package com.android.server.wifi;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
import android.net.NetworkAgent;
import android.net.wifi.ScanResult;
+import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.util.Base64;
import android.util.Log;
import android.util.SparseIntArray;
import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.nano.WifiMetricsProto;
+import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
+import com.android.server.wifi.nano.WifiMetricsProto.StaEvent.ConfigInfo;
import com.android.server.wifi.util.InformationElementUtil;
+import com.android.server.wifi.util.ScanResultUtil;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.Calendar;
+import java.util.LinkedList;
import java.util.List;
/**
@@ -48,6 +61,10 @@
*/
private static final int MAX_RSSI_POLL = 0;
private static final int MIN_RSSI_POLL = -127;
+ public static final int MAX_RSSI_DELTA = 127;
+ public static final int MIN_RSSI_DELTA = -127;
+ /** Maximum time period between ScanResult and RSSI poll to generate rssi delta datapoint */
+ public static final long TIMEOUT_RSSI_DELTA_MILLIS = 3000;
private static final int MIN_WIFI_SCORE = 0;
private static final int MAX_WIFI_SCORE = NetworkAgent.WIFI_BASE_SCORE;
private final Object mLock = new Object();
@@ -55,6 +72,7 @@
private Clock mClock;
private boolean mScreenOn;
private int mWifiState;
+ private Handler mHandler;
/**
* Metrics are stored within an instance of the WifiLog proto during runtime,
* The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during
@@ -81,15 +99,24 @@
private final SparseIntArray mWifiSystemStateEntries = new SparseIntArray();
/** Mapping of RSSI values to counts. */
private final SparseIntArray mRssiPollCounts = new SparseIntArray();
+ /** Mapping of RSSI scan-poll delta values to counts. */
+ private final SparseIntArray mRssiDeltaCounts = new SparseIntArray();
+ /** RSSI of the scan result for the last connection event*/
+ private int mScanResultRssi = 0;
+ /** Boot-relative timestamp when the last candidate scanresult was received, used to calculate
+ RSSI deltas. -1 designates no candidate scanResult being tracked */
+ private long mScanResultRssiTimestampMillis = -1;
/** Mapping of alert reason to the respective alert count. */
private final SparseIntArray mWifiAlertReasonCounts = 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 long mRecordStartTimeSec;
/** Mapping of Wifi Scores to counts */
private final SparseIntArray mWifiScoreCounts = new SparseIntArray();
+ /** Mapping of SoftApManager start SoftAp return codes to counts */
+ private final SparseIntArray mSoftApManagerReturnCodeCounts = new SparseIntArray();
class RouterFingerPrint {
private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
RouterFingerPrint() {
@@ -317,12 +344,20 @@
}
}
- public WifiMetrics(Clock clock) {
+ public WifiMetrics(Clock clock, Looper looper) {
mClock = clock;
mCurrentConnectionEvent = null;
mScreenOn = true;
mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED;
- mRecordStartTimeSec = mClock.elapsedRealtime() / 1000;
+ mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
+
+ mHandler = new Handler(looper) {
+ public void handleMessage(Message msg) {
+ synchronized (mLock) {
+ processMessage(msg);
+ }
+ }
+ };
}
// Values used for indexing SystemStateEntries
@@ -365,15 +400,26 @@
}
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);
+ mScanResultRssiTimestampMillis = -1;
+ if (config != null) {
+ ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
+ if (candidate != null) {
+ // Cache the RSSI of the candidate, as the connection event level is updated
+ // from other sources (polls, bssid_associations) and delta requires the
+ // scanResult rssi
+ mScanResultRssi = candidate.level;
+ mScanResultRssiTimestampMillis = mClock.getElapsedSinceBootMillis();
+ }
+ }
}
}
@@ -423,7 +469,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);
@@ -432,6 +478,9 @@
connectivityFailureCode;
// ConnectionEvent already added to ConnectionEvents List. Safe to null current here
mCurrentConnectionEvent = null;
+ if (!result) {
+ mScanResultRssiTimestampMillis = -1;
+ }
}
}
}
@@ -482,13 +531,13 @@
WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
mCurrentConnectionEvent.mConfigBssid = scanResult.BSSID;
if (scanResult.capabilities != null) {
- if (scanResult.capabilities.contains("WEP")) {
+ if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
- } else if (scanResult.capabilities.contains("PSK")) {
+ } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
- } else if (scanResult.capabilities.contains("EAP")) {
+ } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
}
@@ -497,54 +546,6 @@
scanResult.frequency;
}
- void setNumSavedNetworks(int num) {
- synchronized (mLock) {
- mWifiLogProto.numSavedNetworks = num;
- }
- }
-
- void setNumOpenNetworks(int num) {
- synchronized (mLock) {
- mWifiLogProto.numOpenNetworks = num;
- }
- }
-
- void setNumPersonalNetworks(int num) {
- synchronized (mLock) {
- mWifiLogProto.numPersonalNetworks = num;
- }
- }
-
- void setNumEnterpriseNetworks(int num) {
- synchronized (mLock) {
- mWifiLogProto.numEnterpriseNetworks = num;
- }
- }
-
- void setNumHiddenNetworks(int num) {
- synchronized (mLock) {
- mWifiLogProto.numHiddenNetworks = num;
- }
- }
-
- void setNumPasspointNetworks(int num) {
- synchronized (mLock) {
- mWifiLogProto.numPasspointNetworks = num;
- }
- }
-
- void setNumNetworksAddedByUser(int num) {
- synchronized (mLock) {
- mWifiLogProto.numNetworksAddedByUser = num;
- }
- }
-
- void setNumNetworksAddedByApps(int num) {
- synchronized (mLock) {
- mWifiLogProto.numNetworksAddedByApps = num;
- }
- }
-
void setIsLocationEnabled(boolean enabled) {
synchronized (mLock) {
mWifiLogProto.isLocationEnabled = enabled;
@@ -814,6 +815,16 @@
}
/**
+ * Increment various poll related metrics, and cache performance data for StaEvent logging
+ */
+ public void handlePollResult(WifiInfo wifiInfo) {
+ mLastPollRssi = wifiInfo.getRssi();
+ mLastPollLinkSpeed = wifiInfo.getLinkSpeed();
+ mLastPollFreq = wifiInfo.getFrequency();
+ incrementRssiPollRssiCount(mLastPollRssi);
+ }
+
+ /**
* Increment occurence count of RSSI level from RSSI poll.
* Ignores rssi values outside the bounds of [MIN_RSSI_POLL, MAX_RSSI_POLL]
*/
@@ -824,6 +835,26 @@
synchronized (mLock) {
int count = mRssiPollCounts.get(rssi);
mRssiPollCounts.put(rssi, count + 1);
+ maybeIncrementRssiDeltaCount(rssi - mScanResultRssi);
+ }
+ }
+
+ /**
+ * Increment occurence count of difference between scan result RSSI and the first RSSI poll.
+ * Ignores rssi values outside the bounds of [MIN_RSSI_DELTA, MAX_RSSI_DELTA]
+ * mLock must be held when calling this method.
+ */
+ private void maybeIncrementRssiDeltaCount(int rssi) {
+ // Check if this RSSI poll is close enough to a scan result RSSI to log a delta value
+ if (mScanResultRssiTimestampMillis >= 0) {
+ long timeDelta = mClock.getElapsedSinceBootMillis() - mScanResultRssiTimestampMillis;
+ if (timeDelta <= TIMEOUT_RSSI_DELTA_MILLIS) {
+ if (rssi >= MIN_RSSI_DELTA && rssi <= MAX_RSSI_DELTA) {
+ int count = mRssiDeltaCounts.get(rssi);
+ mRssiDeltaCounts.put(rssi, count + 1);
+ }
+ }
+ mScanResultRssiTimestampMillis = -1;
}
}
@@ -883,10 +914,10 @@
}
}
if (scanResult != null && scanResult.capabilities != null) {
- if (scanResult.capabilities.contains("EAP")) {
+ if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
enterpriseNetworks++;
- } else if (scanResult.capabilities.contains("PSK")
- || scanResult.capabilities.contains("WEP")) {
+ } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)
+ || ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
personalNetworks++;
} else {
openNetworks++;
@@ -920,6 +951,78 @@
}
}
+ /**
+ * Increments occurence of the results from attempting to start SoftAp.
+ * Maps the |result| and WifiManager |failureCode| constant to proto defined SoftApStartResult
+ * codes.
+ */
+ public void incrementSoftApStartResult(boolean result, int failureCode) {
+ synchronized (mLock) {
+ if (result) {
+ int count = mSoftApManagerReturnCodeCounts.get(
+ WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY);
+ mSoftApManagerReturnCodeCounts.put(
+ WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY,
+ count + 1);
+ return;
+ }
+
+ // now increment failure modes - if not explicitly handled, dump into the general
+ // error bucket.
+ if (failureCode == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
+ int count = mSoftApManagerReturnCodeCounts.get(
+ WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL);
+ mSoftApManagerReturnCodeCounts.put(
+ WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL,
+ count + 1);
+ } else {
+ // failure mode not tracked at this time... count as a general error for now.
+ int count = mSoftApManagerReturnCodeCounts.get(
+ WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR);
+ mSoftApManagerReturnCodeCounts.put(
+ WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR,
+ count + 1);
+ }
+ }
+ }
+
+ /**
+ * Increment number of times the HAL crashed.
+ */
+ public void incrementNumHalCrashes() {
+ synchronized (mLock) {
+ mWifiLogProto.numHalCrashes++;
+ }
+ }
+
+ /**
+ * Increment number of times the Wificond crashed.
+ */
+ public void incrementNumWificondCrashes() {
+ synchronized (mLock) {
+ mWifiLogProto.numWificondCrashes++;
+ }
+ }
+
+ /**
+ * Increment number of times the wifi on failed due to an error in HAL.
+ */
+ public void incrementNumWifiOnFailureDueToHal() {
+ synchronized (mLock) {
+ mWifiLogProto.numWifiOnFailureDueToHal++;
+ }
+ }
+
+ /**
+ * Increment number of times the wifi on failed due to an error in wificond.
+ */
+ public void incrementNumWifiOnFailureDueToWificond() {
+ synchronized (mLock) {
+ mWifiLogProto.numWifiOnFailureDueToWificond++;
+ }
+ }
+
+
public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
public static final String CLEAN_DUMP_ARG = "clean";
@@ -933,7 +1036,7 @@
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mLock) {
- if (args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) {
+ if (args != null && args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) {
// Dump serialized WifiLog proto
consolidateProto(true);
for (ConnectionEvent event : mConnectionEventList) {
@@ -1050,7 +1153,7 @@
pw.println("mWifiLogProto.numLastResortWatchdogSuccesses="
+ mWifiLogProto.numLastResortWatchdogSuccesses);
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();
@@ -1058,6 +1161,13 @@
sb.append(mRssiPollCounts.get(i) + " ");
}
pw.println(" " + sb.toString());
+ pw.println("mWifiLogProto.rssiPollDeltaCount: Printing counts for ["
+ + MIN_RSSI_DELTA + ", " + MAX_RSSI_DELTA + "]");
+ sb.setLength(0);
+ for (int i = MIN_RSSI_DELTA; i <= MAX_RSSI_DELTA; i++) {
+ sb.append(mRssiDeltaCounts.get(i) + " ");
+ }
+ pw.println(" " + sb.toString());
pw.print("mWifiLogProto.alertReasonCounts=");
sb.setLength(0);
for (int i = WifiLoggerHal.WIFI_ALERT_REASON_MIN;
@@ -1093,7 +1203,65 @@
for (int i = 0; i <= MAX_WIFI_SCORE; i++) {
pw.print(mWifiScoreCounts.get(i) + " ");
}
+ pw.println(); // add a line after wifi scores
+ pw.println("mWifiLogProto.SoftApManagerReturnCodeCounts:");
+ pw.println(" SUCCESS: " + mSoftApManagerReturnCodeCounts.get(
+ WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY));
+ pw.println(" FAILED_GENERAL_ERROR: " + mSoftApManagerReturnCodeCounts.get(
+ WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR));
+ pw.println(" FAILED_NO_CHANNEL: " + mSoftApManagerReturnCodeCounts.get(
+ WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL));
pw.print("\n");
+ pw.println("mWifiLogProto.numHalCrashes="
+ + mWifiLogProto.numHalCrashes);
+ pw.println("mWifiLogProto.numWificondCrashes="
+ + mWifiLogProto.numWificondCrashes);
+ pw.println("mWifiLogProto.numWifiOnFailureDueToHal="
+ + mWifiLogProto.numWifiOnFailureDueToHal);
+ pw.println("mWifiLogProto.numWifiOnFailureDueToWificond="
+ + mWifiLogProto.numWifiOnFailureDueToWificond);
+ pw.println("StaEventList:");
+ for (StaEvent event : mStaEventList) {
+ pw.println(staEventToString(event));
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Update various counts of saved network types
+ * @param networks List of WifiConfigurations representing all saved networks, must not be null
+ */
+ public void updateSavedNetworks(List<WifiConfiguration> networks) {
+ synchronized (mLock) {
+ mWifiLogProto.numSavedNetworks = networks.size();
+ mWifiLogProto.numOpenNetworks = 0;
+ mWifiLogProto.numPersonalNetworks = 0;
+ mWifiLogProto.numEnterpriseNetworks = 0;
+ mWifiLogProto.numNetworksAddedByUser = 0;
+ mWifiLogProto.numNetworksAddedByApps = 0;
+ mWifiLogProto.numHiddenNetworks = 0;
+ mWifiLogProto.numPasspointNetworks = 0;
+ for (WifiConfiguration config : networks) {
+ if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
+ mWifiLogProto.numOpenNetworks++;
+ } else if (config.isEnterprise()) {
+ mWifiLogProto.numEnterpriseNetworks++;
+ } else {
+ mWifiLogProto.numPersonalNetworks++;
+ }
+ if (config.selfAdded) {
+ mWifiLogProto.numNetworksAddedByUser++;
+ } else {
+ mWifiLogProto.numNetworksAddedByApps++;
+ }
+ if (config.hiddenSSID) {
+ mWifiLogProto.numHiddenNetworks++;
+ }
+ if (config.isPasspoint()) {
+ mWifiLogProto.numPasspointNetworks++;
+ }
}
}
}
@@ -1107,6 +1275,7 @@
private void consolidateProto(boolean incremental) {
List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>();
List<WifiMetricsProto.RssiPollCount> rssis = new ArrayList<>();
+ List<WifiMetricsProto.RssiPollCount> rssiDeltas = new ArrayList<>();
List<WifiMetricsProto.AlertReasonCount> alertReasons = new ArrayList<>();
List<WifiMetricsProto.WifiScoreCount> scores = new ArrayList<>();
synchronized (mLock) {
@@ -1152,7 +1321,7 @@
mWifiLogProto.wifiSystemStateEntries[i].isScreenOn =
(mWifiSystemStateEntries.keyAt(i) % 2) > 0;
}
- mWifiLogProto.recordDurationSec = (int) ((mClock.elapsedRealtime() / 1000)
+ mWifiLogProto.recordDurationSec = (int) ((mClock.getElapsedSinceBootMillis() / 1000)
- mRecordStartTimeSec);
/**
@@ -1168,6 +1337,18 @@
mWifiLogProto.rssiPollRssiCount = rssis.toArray(mWifiLogProto.rssiPollRssiCount);
/**
+ * Convert the SparseIntArray of RSSI delta rssi's and counts to the proto's repeated
+ * IntKeyVal array.
+ */
+ for (int i = 0; i < mRssiDeltaCounts.size(); i++) {
+ WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount();
+ keyVal.rssi = mRssiDeltaCounts.keyAt(i);
+ keyVal.count = mRssiDeltaCounts.valueAt(i);
+ rssiDeltas.add(keyVal);
+ }
+ mWifiLogProto.rssiPollDeltaCount = rssiDeltas.toArray(mWifiLogProto.rssiPollDeltaCount);
+
+ /**
* Convert the SparseIntArray of alert reasons and counts to the proto's repeated
* IntKeyVal array.
*/
@@ -1178,6 +1359,7 @@
alertReasons.add(keyVal);
}
mWifiLogProto.alertReasonCount = alertReasons.toArray(mWifiLogProto.alertReasonCount);
+
/**
* Convert the SparseIntArray of Wifi Score and counts to proto's repeated
* IntKeyVal array.
@@ -1189,6 +1371,23 @@
scores.add(keyVal);
}
mWifiLogProto.wifiScoreCount = scores.toArray(mWifiLogProto.wifiScoreCount);
+
+ /**
+ * Convert the SparseIntArray of SoftAp Return codes and counts to proto's repeated
+ * IntKeyVal array.
+ */
+ int codeCounts = mSoftApManagerReturnCodeCounts.size();
+ mWifiLogProto.softApReturnCode = new WifiMetricsProto.SoftApReturnCodeCount[codeCounts];
+ for (int sapCode = 0; sapCode < codeCounts; sapCode++) {
+ mWifiLogProto.softApReturnCode[sapCode] =
+ new WifiMetricsProto.SoftApReturnCodeCount();
+ mWifiLogProto.softApReturnCode[sapCode].startResult =
+ mSoftApManagerReturnCodeCounts.keyAt(sapCode);
+ mWifiLogProto.softApReturnCode[sapCode].count =
+ mSoftApManagerReturnCodeCounts.valueAt(sapCode);
+ }
+
+ mWifiLogProto.staEventList = mStaEventList.toArray(mWifiLogProto.staEventList);
}
}
@@ -1203,11 +1402,15 @@
}
mScanReturnEntries.clear();
mWifiSystemStateEntries.clear();
- mRecordStartTimeSec = mClock.elapsedRealtime() / 1000;
+ mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
mRssiPollCounts.clear();
+ mRssiDeltaCounts.clear();
mWifiAlertReasonCounts.clear();
mWifiScoreCounts.clear();
mWifiLogProto.clear();
+ mScanResultRssiTimestampMillis = -1;
+ mSoftApManagerReturnCodeCounts.clear();
+ mStaEventList.clear();
}
}
@@ -1228,4 +1431,370 @@
mWifiState = wifiState;
}
}
+
+ /**
+ * Message handler for interesting WifiMonitor messages. Generates StaEvents
+ */
+ private void processMessage(Message msg) {
+ StaEvent event = new StaEvent();
+ boolean logEvent = true;
+ switch (msg.what) {
+ case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
+ event.type = StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT;
+ event.associationTimedOut = msg.arg1 > 0 ? true : false;
+ event.status = msg.arg2;
+ break;
+ case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
+ event.type = StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT;
+ switch (msg.arg2) {
+ case WifiManager.ERROR_AUTH_FAILURE_NONE:
+ event.authFailureReason = StaEvent.AUTH_FAILURE_NONE;
+ break;
+ case WifiManager.ERROR_AUTH_FAILURE_TIMEOUT:
+ event.authFailureReason = StaEvent.AUTH_FAILURE_TIMEOUT;
+ break;
+ case WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD:
+ event.authFailureReason = StaEvent.AUTH_FAILURE_WRONG_PSWD;
+ break;
+ case WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE:
+ event.authFailureReason = StaEvent.AUTH_FAILURE_EAP_FAILURE;
+ break;
+ default:
+ break;
+ }
+ break;
+ case WifiMonitor.NETWORK_CONNECTION_EVENT:
+ event.type = StaEvent.TYPE_NETWORK_CONNECTION_EVENT;
+ break;
+ case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+ event.type = StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT;
+ event.reason = msg.arg2;
+ event.localGen = msg.arg1 == 0 ? false : true;
+ break;
+ case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
+ logEvent = false;
+ StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
+ mSupplicantStateChangeBitmask |= supplicantStateToBit(stateChangeResult.state);
+ break;
+ case WifiStateMachine.CMD_ASSOCIATED_BSSID:
+ event.type = StaEvent.TYPE_CMD_ASSOCIATED_BSSID;
+ break;
+ case WifiStateMachine.CMD_TARGET_BSSID:
+ event.type = StaEvent.TYPE_CMD_TARGET_BSSID;
+ break;
+ default:
+ return;
+ }
+ if (logEvent) {
+ addStaEvent(event);
+ }
+ }
+ /**
+ * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
+ * generated event types, which are logged through 'sendMessage'
+ * @param type StaEvent.EventType describing the event
+ */
+ public void logStaEvent(int type) {
+ logStaEvent(type, StaEvent.DISCONNECT_UNKNOWN, null);
+ }
+ /**
+ * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
+ * generated event types, which are logged through 'sendMessage'
+ * @param type StaEvent.EventType describing the event
+ * @param config WifiConfiguration for a framework initiated connection attempt
+ */
+ public void logStaEvent(int type, WifiConfiguration config) {
+ logStaEvent(type, StaEvent.DISCONNECT_UNKNOWN, config);
+ }
+ /**
+ * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
+ * generated event types, which are logged through 'sendMessage'
+ * @param type StaEvent.EventType describing the event
+ * @param frameworkDisconnectReason StaEvent.FrameworkDisconnectReason explaining why framework
+ * initiated a FRAMEWORK_DISCONNECT
+ */
+ public void logStaEvent(int type, int frameworkDisconnectReason) {
+ logStaEvent(type, frameworkDisconnectReason, null);
+ }
+ /**
+ * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
+ * generated event types, which are logged through 'sendMessage'
+ * @param type StaEvent.EventType describing the event
+ * @param frameworkDisconnectReason StaEvent.FrameworkDisconnectReason explaining why framework
+ * initiated a FRAMEWORK_DISCONNECT
+ * @param config WifiConfiguration for a framework initiated connection attempt
+ */
+ public void logStaEvent(int type, int frameworkDisconnectReason, WifiConfiguration config) {
+ switch (type) {
+ case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL:
+ case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST:
+ case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST:
+ case StaEvent.TYPE_CMD_START_CONNECT:
+ case StaEvent.TYPE_CMD_START_ROAM:
+ case StaEvent.TYPE_CONNECT_NETWORK:
+ case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK:
+ case StaEvent.TYPE_FRAMEWORK_DISCONNECT:
+ break;
+ default:
+ Log.e(TAG, "Unknown StaEvent:" + type);
+ return;
+ }
+ StaEvent event = new StaEvent();
+ event.type = type;
+ if (frameworkDisconnectReason != StaEvent.DISCONNECT_UNKNOWN) {
+ event.frameworkDisconnectReason = frameworkDisconnectReason;
+ }
+ event.configInfo = createConfigInfo(config);
+ addStaEvent(event);
+ }
+
+ private void addStaEvent(StaEvent staEvent) {
+ staEvent.startTimeMillis = mClock.getElapsedSinceBootMillis();
+ staEvent.lastRssi = mLastPollRssi;
+ staEvent.lastFreq = mLastPollFreq;
+ staEvent.lastLinkSpeed = mLastPollLinkSpeed;
+ staEvent.supplicantStateChangesBitmask = mSupplicantStateChangeBitmask;
+ mSupplicantStateChangeBitmask = 0;
+ mLastPollRssi = -127;
+ mLastPollFreq = -1;
+ mLastPollLinkSpeed = -1;
+ mStaEventList.add(staEvent);
+ // Prune StaEventList if it gets too long
+ if (mStaEventList.size() > MAX_STA_EVENTS) mStaEventList.remove();
+ }
+
+ private ConfigInfo createConfigInfo(WifiConfiguration config) {
+ if (config == null) return null;
+ ConfigInfo info = new ConfigInfo();
+ info.allowedKeyManagement = bitSetToInt(config.allowedKeyManagement);
+ info.allowedProtocols = bitSetToInt(config.allowedProtocols);
+ info.allowedAuthAlgorithms = bitSetToInt(config.allowedAuthAlgorithms);
+ info.allowedPairwiseCiphers = bitSetToInt(config.allowedPairwiseCiphers);
+ info.allowedGroupCiphers = bitSetToInt(config.allowedGroupCiphers);
+ info.hiddenSsid = config.hiddenSSID;
+ info.isPasspoint = config.isPasspoint();
+ info.isEphemeral = config.isEphemeral();
+ info.hasEverConnected = config.getNetworkSelectionStatus().getHasEverConnected();
+ ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
+ if (candidate != null) {
+ info.scanRssi = candidate.level;
+ info.scanFreq = candidate.frequency;
+ }
+ return info;
+ }
+
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ // Rather than generate a StaEvent for each SUPPLICANT_STATE_CHANGE, cache these in a bitmask
+ // and attach it to the next event which is generated.
+ private int mSupplicantStateChangeBitmask = 0;
+
+ /**
+ * Converts a SupplicantState value to a single bit, with position defined by
+ * {@code StaEvent.SupplicantState}
+ */
+ public static int supplicantStateToBit(SupplicantState state) {
+ switch(state) {
+ case DISCONNECTED:
+ return 1 << StaEvent.STATE_DISCONNECTED;
+ case INTERFACE_DISABLED:
+ return 1 << StaEvent.STATE_INTERFACE_DISABLED;
+ case INACTIVE:
+ return 1 << StaEvent.STATE_INACTIVE;
+ case SCANNING:
+ return 1 << StaEvent.STATE_SCANNING;
+ case AUTHENTICATING:
+ return 1 << StaEvent.STATE_AUTHENTICATING;
+ case ASSOCIATING:
+ return 1 << StaEvent.STATE_ASSOCIATING;
+ case ASSOCIATED:
+ return 1 << StaEvent.STATE_ASSOCIATED;
+ case FOUR_WAY_HANDSHAKE:
+ return 1 << StaEvent.STATE_FOUR_WAY_HANDSHAKE;
+ case GROUP_HANDSHAKE:
+ return 1 << StaEvent.STATE_GROUP_HANDSHAKE;
+ case COMPLETED:
+ return 1 << StaEvent.STATE_COMPLETED;
+ case DORMANT:
+ return 1 << StaEvent.STATE_DORMANT;
+ case UNINITIALIZED:
+ return 1 << StaEvent.STATE_UNINITIALIZED;
+ case INVALID:
+ return 1 << StaEvent.STATE_INVALID;
+ default:
+ Log.wtf(TAG, "Got unknown supplicant state: " + state.ordinal());
+ return 0;
+ }
+ }
+
+ private static String supplicantStateChangesBitmaskToString(int mask) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("SUPPLICANT_STATE_CHANGE_EVENTS: {");
+ if ((mask & (1 << StaEvent.STATE_DISCONNECTED)) > 0) sb.append(" DISCONNECTED");
+ if ((mask & (1 << StaEvent.STATE_INTERFACE_DISABLED)) > 0) sb.append(" INTERFACE_DISABLED");
+ if ((mask & (1 << StaEvent.STATE_INACTIVE)) > 0) sb.append(" INACTIVE");
+ if ((mask & (1 << StaEvent.STATE_SCANNING)) > 0) sb.append(" SCANNING");
+ if ((mask & (1 << StaEvent.STATE_AUTHENTICATING)) > 0) sb.append(" AUTHENTICATING");
+ if ((mask & (1 << StaEvent.STATE_ASSOCIATING)) > 0) sb.append(" ASSOCIATING");
+ if ((mask & (1 << StaEvent.STATE_ASSOCIATED)) > 0) sb.append(" ASSOCIATED");
+ if ((mask & (1 << StaEvent.STATE_FOUR_WAY_HANDSHAKE)) > 0) sb.append(" FOUR_WAY_HANDSHAKE");
+ if ((mask & (1 << StaEvent.STATE_GROUP_HANDSHAKE)) > 0) sb.append(" GROUP_HANDSHAKE");
+ if ((mask & (1 << StaEvent.STATE_COMPLETED)) > 0) sb.append(" COMPLETED");
+ if ((mask & (1 << StaEvent.STATE_DORMANT)) > 0) sb.append(" DORMANT");
+ if ((mask & (1 << StaEvent.STATE_UNINITIALIZED)) > 0) sb.append(" UNINITIALIZED");
+ if ((mask & (1 << StaEvent.STATE_INVALID)) > 0) sb.append(" INVALID");
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Returns a human readable string from a Sta Event. Only adds information relevant to the event
+ * type.
+ */
+ public static String staEventToString(StaEvent event) {
+ if (event == null) return "<NULL>";
+ StringBuilder sb = new StringBuilder();
+ Long time = event.startTimeMillis;
+ sb.append(String.format("%9d ", time.longValue())).append(" ");
+ switch (event.type) {
+ case StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT:
+ sb.append("ASSOCIATION_REJECTION_EVENT:")
+ .append(" timedOut=").append(event.associationTimedOut)
+ .append(" status=").append(event.status).append(":")
+ .append(ISupplicantStaIfaceCallback.StatusCode.toString(event.status));
+ break;
+ case StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT:
+ sb.append("AUTHENTICATION_FAILURE_EVENT: reason=").append(event.authFailureReason)
+ .append(":").append(authFailureReasonToString(event.authFailureReason));
+ break;
+ case StaEvent.TYPE_NETWORK_CONNECTION_EVENT:
+ sb.append("NETWORK_CONNECTION_EVENT:");
+ break;
+ case StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT:
+ sb.append("NETWORK_DISCONNECTION_EVENT:")
+ .append(" local_gen=").append(event.localGen)
+ .append(" reason=").append(event.reason).append(":")
+ .append(ISupplicantStaIfaceCallback.ReasonCode.toString(
+ (event.reason >= 0 ? event.reason : -1 * event.reason)));
+ break;
+ case StaEvent.TYPE_CMD_ASSOCIATED_BSSID:
+ sb.append("CMD_ASSOCIATED_BSSID:");
+ break;
+ case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL:
+ sb.append("CMD_IP_CONFIGURATION_SUCCESSFUL:");
+ break;
+ case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST:
+ sb.append("CMD_IP_CONFIGURATION_LOST:");
+ break;
+ case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST:
+ sb.append("CMD_IP_REACHABILITY_LOST:");
+ break;
+ case StaEvent.TYPE_CMD_TARGET_BSSID:
+ sb.append("CMD_TARGET_BSSID:");
+ break;
+ case StaEvent.TYPE_CMD_START_CONNECT:
+ sb.append("CMD_START_CONNECT:");
+ break;
+ case StaEvent.TYPE_CMD_START_ROAM:
+ sb.append("CMD_START_ROAM:");
+ break;
+ case StaEvent.TYPE_CONNECT_NETWORK:
+ sb.append("CONNECT_NETWORK:");
+ break;
+ case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK:
+ sb.append("NETWORK_AGENT_VALID_NETWORK:");
+ break;
+ case StaEvent.TYPE_FRAMEWORK_DISCONNECT:
+ sb.append("FRAMEWORK_DISCONNECT:")
+ .append(" reason=")
+ .append(frameworkDisconnectReasonToString(event.frameworkDisconnectReason));
+ break;
+ default:
+ sb.append("UNKNOWN " + event.type + ":");
+ break;
+ }
+ if (event.lastRssi != -127) sb.append(" lastRssi=").append(event.lastRssi);
+ if (event.lastFreq != -1) sb.append(" lastFreq=").append(event.lastFreq);
+ if (event.lastLinkSpeed != -1) sb.append(" lastLinkSpeed=").append(event.lastLinkSpeed);
+ if (event.supplicantStateChangesBitmask != 0) {
+ sb.append("\n ").append(supplicantStateChangesBitmaskToString(
+ event.supplicantStateChangesBitmask));
+ }
+ if (event.configInfo != null) {
+ sb.append("\n ").append(configInfoToString(event.configInfo));
+ }
+
+ return sb.toString();
+ }
+
+ private static String authFailureReasonToString(int authFailureReason) {
+ switch (authFailureReason) {
+ case StaEvent.AUTH_FAILURE_NONE:
+ return "ERROR_AUTH_FAILURE_NONE";
+ case StaEvent.AUTH_FAILURE_TIMEOUT:
+ return "ERROR_AUTH_FAILURE_TIMEOUT";
+ case StaEvent.AUTH_FAILURE_WRONG_PSWD:
+ return "ERROR_AUTH_FAILURE_WRONG_PSWD";
+ case StaEvent.AUTH_FAILURE_EAP_FAILURE:
+ return "ERROR_AUTH_FAILURE_EAP_FAILURE";
+ default:
+ return "";
+ }
+ }
+
+ private static String frameworkDisconnectReasonToString(int frameworkDisconnectReason) {
+ switch (frameworkDisconnectReason) {
+ case StaEvent.DISCONNECT_API:
+ return "DISCONNECT_API";
+ case StaEvent.DISCONNECT_GENERIC:
+ return "DISCONNECT_GENERIC";
+ case StaEvent.DISCONNECT_UNWANTED:
+ return "DISCONNECT_UNWANTED";
+ case StaEvent.DISCONNECT_ROAM_WATCHDOG_TIMER:
+ return "DISCONNECT_ROAM_WATCHDOG_TIMER";
+ case StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST:
+ return "DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST";
+ case StaEvent.DISCONNECT_RESET_SIM_NETWORKS:
+ return "DISCONNECT_RESET_SIM_NETWORKS";
+ default:
+ return "DISCONNECT_UNKNOWN=" + frameworkDisconnectReason;
+ }
+ }
+
+ private static String configInfoToString(ConfigInfo info) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("ConfigInfo:")
+ .append(" allowed_key_management=").append(info.allowedKeyManagement)
+ .append(" allowed_protocols=").append(info.allowedProtocols)
+ .append(" allowed_auth_algorithms=").append(info.allowedAuthAlgorithms)
+ .append(" allowed_pairwise_ciphers=").append(info.allowedPairwiseCiphers)
+ .append(" allowed_group_ciphers=").append(info.allowedGroupCiphers)
+ .append(" hidden_ssid=").append(info.hiddenSsid)
+ .append(" is_passpoint=").append(info.isPasspoint)
+ .append(" is_ephemeral=").append(info.isEphemeral)
+ .append(" has_ever_connected=").append(info.hasEverConnected)
+ .append(" scan_rssi=").append(info.scanRssi)
+ .append(" scan_freq=").append(info.scanFreq);
+ return sb.toString();
+ }
+
+ public static final int MAX_STA_EVENTS = 512;
+ private LinkedList<StaEvent> mStaEventList = new LinkedList<StaEvent>();
+ private int mLastPollRssi = -127;
+ private int mLastPollLinkSpeed = -1;
+ private int mLastPollFreq = -1;
+
+ /**
+ * Converts the first 31 bits of a BitSet to a little endian int
+ */
+ private static int bitSetToInt(BitSet bits) {
+ int value = 0;
+ int nBits = bits.length() < 31 ? bits.length() : 31;
+ for (int i = 0; i < nBits; i++) {
+ value += bits.get(i) ? (1 << i) : 0;
+ }
+ return value;
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java
index 1f2b397..b2fc56e 100644
--- a/service/java/com/android/server/wifi/WifiMonitor.java
+++ b/service/java/com/android/server/wifi/WifiMonitor.java
@@ -16,427 +16,37 @@
package com.android.server.wifi;
-import android.net.NetworkInfo;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiSsid;
-import android.net.wifi.p2p.WifiP2pConfig;
-import android.net.wifi.p2p.WifiP2pDevice;
-import android.net.wifi.p2p.WifiP2pGroup;
-import android.net.wifi.p2p.WifiP2pProvDiscEvent;
-import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
import android.os.Handler;
import android.os.Message;
-import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.Base64;
-import android.util.LocalLog;
import android.util.Log;
import android.util.SparseArray;
-import com.android.server.wifi.hotspot2.IconEvent;
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Protocol;
import com.android.internal.util.StateMachine;
+import com.android.server.wifi.hotspot2.AnqpEvent;
+import com.android.server.wifi.hotspot2.IconEvent;
+import com.android.server.wifi.hotspot2.WnmData;
+import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData;
-import java.io.IOException;
import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Listens for events from the wpa_supplicant server, and passes them on
- * to the {@link StateMachine} for handling. Runs in its own thread.
+ * to the {@link StateMachine} for handling.
*
* @hide
*/
public class WifiMonitor {
-
- private static boolean DBG = false;
- private static final boolean VDBG = false;
private static final String TAG = "WifiMonitor";
- /** Events we receive from the supplicant daemon */
-
- private static final int CONNECTED = 1;
- private static final int DISCONNECTED = 2;
- private static final int STATE_CHANGE = 3;
- private static final int SCAN_RESULTS = 4;
- private static final int LINK_SPEED = 5;
- private static final int TERMINATING = 6;
- private static final int DRIVER_STATE = 7;
- private static final int EAP_FAILURE = 8;
- private static final int ASSOC_REJECT = 9;
- private static final int SSID_TEMP_DISABLE = 10;
- private static final int SSID_REENABLE = 11;
- private static final int BSS_ADDED = 12;
- private static final int BSS_REMOVED = 13;
- private static final int UNKNOWN = 14;
- private static final int SCAN_FAILED = 15;
-
- /** All events coming from the supplicant start with this prefix */
- private static final String EVENT_PREFIX_STR = "CTRL-EVENT-";
- private static final int EVENT_PREFIX_LEN_STR = EVENT_PREFIX_STR.length();
-
- /** All events coming from the supplicant start with this prefix */
- private static final String REQUEST_PREFIX_STR = "CTRL-REQ-";
- private static final int REQUEST_PREFIX_LEN_STR = REQUEST_PREFIX_STR.length();
-
-
- /** All WPA events coming from the supplicant start with this prefix */
- private static final String WPA_EVENT_PREFIX_STR = "WPA:";
- private static final String PASSWORD_MAY_BE_INCORRECT_STR =
- "pre-shared key may be incorrect";
-
- /* WPS events */
- private static final String WPS_SUCCESS_STR = "WPS-SUCCESS";
-
- /* Format: WPS-FAIL msg=%d [config_error=%d] [reason=%d (%s)] */
- private static final String WPS_FAIL_STR = "WPS-FAIL";
- private static final String WPS_FAIL_PATTERN =
- "WPS-FAIL msg=\\d+(?: config_error=(\\d+))?(?: reason=(\\d+))?";
-
- /* config error code values for config_error=%d */
- private static final int CONFIG_MULTIPLE_PBC_DETECTED = 12;
- private static final int CONFIG_AUTH_FAILURE = 18;
-
- /* reason code values for reason=%d */
- private static final int REASON_TKIP_ONLY_PROHIBITED = 1;
- private static final int REASON_WEP_PROHIBITED = 2;
-
- private static final String WPS_OVERLAP_STR = "WPS-OVERLAP-DETECTED";
- private static final String WPS_TIMEOUT_STR = "WPS-TIMEOUT";
-
- /* Hotspot 2.0 ANQP query events */
- private static final String GAS_QUERY_PREFIX_STR = "GAS-QUERY-";
- private static final String GAS_QUERY_START_STR = "GAS-QUERY-START";
- private static final String GAS_QUERY_DONE_STR = "GAS-QUERY-DONE";
- private static final String RX_HS20_ANQP_ICON_STR = "RX-HS20-ANQP-ICON";
- private static final int RX_HS20_ANQP_ICON_STR_LEN = RX_HS20_ANQP_ICON_STR.length();
-
- /* Hotspot 2.0 events */
- private static final String HS20_PREFIX_STR = "HS20-";
- public static final String HS20_SUB_REM_STR = "HS20-SUBSCRIPTION-REMEDIATION";
- public static final String HS20_DEAUTH_STR = "HS20-DEAUTH-IMMINENT-NOTICE";
-
- private static final String IDENTITY_STR = "IDENTITY";
-
- private static final String SIM_STR = "SIM";
-
-
- //used to debug and detect if we miss an event
- private static int eventLogCounter = 0;
-
- /**
- * Names of events from wpa_supplicant (minus the prefix). In the
- * format descriptions, * "<code>x</code>"
- * designates a dynamic value that needs to be parsed out from the event
- * string
- */
- /**
- * <pre>
- * CTRL-EVENT-CONNECTED - Connection to xx:xx:xx:xx:xx:xx completed
- * </pre>
- * <code>xx:xx:xx:xx:xx:xx</code> is the BSSID of the associated access point
- */
- private static final String CONNECTED_STR = "CONNECTED";
- private static final String ConnectPrefix = "Connection to ";
- private static final String ConnectSuffix = " completed";
-
- /**
- * <pre>
- * CTRL-EVENT-DISCONNECTED - Disconnect event - remove keys
- * </pre>
- */
- private static final String DISCONNECTED_STR = "DISCONNECTED";
- /**
- * <pre>
- * CTRL-EVENT-STATE-CHANGE x
- * </pre>
- * <code>x</code> is the numerical value of the new state.
- */
- private static final String STATE_CHANGE_STR = "STATE-CHANGE";
- /**
- * <pre>
- * CTRL-EVENT-SCAN-RESULTS ready
- * </pre>
- */
- private static final String SCAN_RESULTS_STR = "SCAN-RESULTS";
-
- /**
- * <pre>
- * CTRL-EVENT-SCAN-FAILED ret=code[ retry=1]
- * </pre>
- */
- private static final String SCAN_FAILED_STR = "SCAN-FAILED";
-
- /**
- * <pre>
- * CTRL-EVENT-LINK-SPEED x Mb/s
- * </pre>
- * {@code x} is the link speed in Mb/sec.
- */
- private static final String LINK_SPEED_STR = "LINK-SPEED";
- /**
- * <pre>
- * CTRL-EVENT-TERMINATING - signal x
- * </pre>
- * <code>x</code> is the signal that caused termination.
- */
- private static final String TERMINATING_STR = "TERMINATING";
- /**
- * <pre>
- * CTRL-EVENT-DRIVER-STATE state
- * </pre>
- * <code>state</code> can be HANGED
- */
- private static final String DRIVER_STATE_STR = "DRIVER-STATE";
- /**
- * <pre>
- * CTRL-EVENT-EAP-FAILURE EAP authentication failed
- * </pre>
- */
- private static final String EAP_FAILURE_STR = "EAP-FAILURE";
-
- /**
- * This indicates an authentication failure on EAP FAILURE event
- */
- private static final String EAP_AUTH_FAILURE_STR = "EAP authentication failed";
-
- /* EAP authentication timeout events */
- private static final String AUTH_EVENT_PREFIX_STR = "Authentication with";
- private static final String AUTH_TIMEOUT_STR = "timed out.";
-
- /**
- * This indicates an assoc reject event
- */
- private static final String ASSOC_REJECT_STR = "ASSOC-REJECT";
-
- /**
- * This indicates auth or association failure bad enough so as network got disabled
- * - WPA_PSK auth failure suspecting shared key mismatch
- * - failed multiple Associations
- */
- private static final String TEMP_DISABLED_STR = "SSID-TEMP-DISABLED";
-
- /**
- * This indicates a previously disabled SSID was reenabled by supplicant
- */
- private static final String REENABLED_STR = "SSID-REENABLED";
-
- /**
- * This indicates supplicant found a given BSS
- */
- private static final String BSS_ADDED_STR = "BSS-ADDED";
-
- /**
- * This indicates supplicant removed a given BSS
- */
- private static final String BSS_REMOVED_STR = "BSS-REMOVED";
-
- /**
- * Regex pattern for extracting an Ethernet-style MAC address from a string.
- * Matches a strings like the following:<pre>
- * CTRL-EVENT-CONNECTED - Connection to 00:1e:58:ec:d5:6d completed (reauth) [id=1 id_str=]</pre>
- */
- private static Pattern mConnectedEventPattern =
- Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) .* \\[id=([0-9]+) ");
-
- /**
- * Regex pattern for extracting an Ethernet-style MAC address from a string.
- * Matches a strings like the following:<pre>
- * CTRL-EVENT-DISCONNECTED - bssid=ac:22:0b:24:70:74 reason=3 locally_generated=1
- */
- private static Pattern mDisconnectedEventPattern =
- Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) +" +
- "reason=([0-9]+) +locally_generated=([0-1])");
-
- /**
- * Regex pattern for extracting an Ethernet-style MAC address from a string.
- * Matches a strings like the following:<pre>
- * CTRL-EVENT-ASSOC-REJECT - bssid=ac:22:0b:24:70:74 status_code=1
- */
- private static Pattern mAssocRejectEventPattern =
- Pattern.compile("((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) +" +
- "status_code=([0-9]+)");
-
- /**
- * Regex pattern for extracting an Ethernet-style MAC address from a string.
- * Matches a strings like the following:<pre>
- * IFNAME=wlan0 Trying to associate with 6c:f3:7f:ae:87:71
- */
- private static final String TARGET_BSSID_STR = "Trying to associate with ";
-
- private static Pattern mTargetBSSIDPattern =
- Pattern.compile("Trying to associate with ((?:[0-9a-f]{2}:){5}[0-9a-f]{2}).*");
-
- /**
- * Regex pattern for extracting an Ethernet-style MAC address from a string.
- * Matches a strings like the following:<pre>
- * IFNAME=wlan0 Associated with 6c:f3:7f:ae:87:71
- */
- private static final String ASSOCIATED_WITH_STR = "Associated with ";
-
- private static Pattern mAssociatedPattern =
- Pattern.compile("Associated with ((?:[0-9a-f]{2}:){5}[0-9a-f]{2}).*");
-
- /**
- * Regex pattern for extracting an external GSM sim authentication request from a string.
- * Matches a strings like the following:<pre>
- * CTRL-REQ-SIM-<network id>:GSM-AUTH:<RAND1>:<RAND2>[:<RAND3>] needed for SSID <SSID>
- * This pattern should find
- * 0 - id
- * 1 - Rand1
- * 2 - Rand2
- * 3 - Rand3
- * 4 - SSID
- */
- private static Pattern mRequestGsmAuthPattern =
- Pattern.compile("SIM-([0-9]*):GSM-AUTH((:[0-9a-f]+)+) needed for SSID (.+)");
-
- /**
- * Regex pattern for extracting an external 3G sim authentication request from a string.
- * Matches a strings like the following:<pre>
- * CTRL-REQ-SIM-<network id>:UMTS-AUTH:<RAND>:<AUTN> needed for SSID <SSID>
- * This pattern should find
- * 1 - id
- * 2 - Rand
- * 3 - Autn
- * 4 - SSID
- */
- private static Pattern mRequestUmtsAuthPattern =
- Pattern.compile("SIM-([0-9]*):UMTS-AUTH:([0-9a-f]+):([0-9a-f]+) needed for SSID (.+)");
-
- /**
- * Regex pattern for extracting SSIDs from request identity string.
- * Matches a strings like the following:<pre>
- * CTRL-REQ-IDENTITY-xx:Identity needed for SSID XXXX</pre>
- */
- private static Pattern mRequestIdentityPattern =
- Pattern.compile("IDENTITY-([0-9]+):Identity needed for SSID (.+)");
-
- /** P2P events */
- private static final String P2P_EVENT_PREFIX_STR = "P2P";
-
- /* P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13 pri_dev_type=1-0050F204-1
- name='p2p-TEST1' config_methods=0x188 dev_capab=0x27 group_capab=0x0 */
- private static final String P2P_DEVICE_FOUND_STR = "P2P-DEVICE-FOUND";
-
- /* P2P-DEVICE-LOST p2p_dev_addr=42:fc:89:e1:e2:27 */
- private static final String P2P_DEVICE_LOST_STR = "P2P-DEVICE-LOST";
-
- /* P2P-FIND-STOPPED */
- private static final String P2P_FIND_STOPPED_STR = "P2P-FIND-STOPPED";
-
- /* P2P-GO-NEG-REQUEST 42:fc:89:a8:96:09 dev_passwd_id=4 */
- private static final String P2P_GO_NEG_REQUEST_STR = "P2P-GO-NEG-REQUEST";
-
- private static final String P2P_GO_NEG_SUCCESS_STR = "P2P-GO-NEG-SUCCESS";
-
- /* P2P-GO-NEG-FAILURE status=x */
- private static final String P2P_GO_NEG_FAILURE_STR = "P2P-GO-NEG-FAILURE";
-
- private static final String P2P_GROUP_FORMATION_SUCCESS_STR =
- "P2P-GROUP-FORMATION-SUCCESS";
-
- private static final String P2P_GROUP_FORMATION_FAILURE_STR =
- "P2P-GROUP-FORMATION-FAILURE";
-
- /* P2P-GROUP-STARTED p2p-wlan0-0 [client|GO] ssid="DIRECT-W8" freq=2437
- [psk=2182b2e50e53f260d04f3c7b25ef33c965a3291b9b36b455a82d77fd82ca15bc|passphrase="fKG4jMe3"]
- go_dev_addr=fa:7b:7a:42:02:13 [PERSISTENT] */
- private static final String P2P_GROUP_STARTED_STR = "P2P-GROUP-STARTED";
-
- /* P2P-GROUP-REMOVED p2p-wlan0-0 [client|GO] reason=REQUESTED */
- private static final String P2P_GROUP_REMOVED_STR = "P2P-GROUP-REMOVED";
-
- /* P2P-INVITATION-RECEIVED sa=fa:7b:7a:42:02:13 go_dev_addr=f8:7b:7a:42:02:13
- bssid=fa:7b:7a:42:82:13 unknown-network */
- private static final String P2P_INVITATION_RECEIVED_STR = "P2P-INVITATION-RECEIVED";
-
- /* P2P-INVITATION-RESULT status=1 */
- private static final String P2P_INVITATION_RESULT_STR = "P2P-INVITATION-RESULT";
-
- /* P2P-PROV-DISC-PBC-REQ 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
- pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
- group_capab=0x0 */
- private static final String P2P_PROV_DISC_PBC_REQ_STR = "P2P-PROV-DISC-PBC-REQ";
-
- /* P2P-PROV-DISC-PBC-RESP 02:12:47:f2:5a:36 */
- private static final String P2P_PROV_DISC_PBC_RSP_STR = "P2P-PROV-DISC-PBC-RESP";
-
- /* P2P-PROV-DISC-ENTER-PIN 42:fc:89:e1:e2:27 p2p_dev_addr=42:fc:89:e1:e2:27
- pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
- group_capab=0x0 */
- private static final String P2P_PROV_DISC_ENTER_PIN_STR = "P2P-PROV-DISC-ENTER-PIN";
- /* P2P-PROV-DISC-SHOW-PIN 42:fc:89:e1:e2:27 44490607 p2p_dev_addr=42:fc:89:e1:e2:27
- pri_dev_type=1-0050F204-1 name='p2p-TEST2' config_methods=0x188 dev_capab=0x27
- group_capab=0x0 */
- private static final String P2P_PROV_DISC_SHOW_PIN_STR = "P2P-PROV-DISC-SHOW-PIN";
- /* P2P-PROV-DISC-FAILURE p2p_dev_addr=42:fc:89:e1:e2:27 */
- private static final String P2P_PROV_DISC_FAILURE_STR = "P2P-PROV-DISC-FAILURE";
-
- /*
- * Protocol format is as follows.<br>
- * See the Table.62 in the WiFi Direct specification for the detail.
- * ______________________________________________________________
- * | Length(2byte) | Type(1byte) | TransId(1byte)}|
- * ______________________________________________________________
- * | status(1byte) | vendor specific(variable) |
- *
- * P2P-SERV-DISC-RESP 42:fc:89:e1:e2:27 1 0300000101
- * length=3, service type=0(ALL Service), transaction id=1,
- * status=1(service protocol type not available)<br>
- *
- * P2P-SERV-DISC-RESP 42:fc:89:e1:e2:27 1 0300020201
- * length=3, service type=2(UPnP), transaction id=2,
- * status=1(service protocol type not available)
- *
- * P2P-SERV-DISC-RESP 42:fc:89:e1:e2:27 1 990002030010757569643a3131323
- * 2646534652d383537342d353961622d393332322d3333333435363738393034343a3
- * a75726e3a736368656d61732d75706e702d6f72673a736572766963653a436f6e746
- * 56e744469726563746f72793a322c757569643a36383539646564652d383537342d3
- * 53961622d393333322d3132333435363738393031323a3a75706e703a726f6f74646
- * 576696365
- * length=153,type=2(UPnP),transaction id=3,status=0
- *
- * UPnP Protocol format is as follows.
- * ______________________________________________________
- * | Version (1) | USN (Variable) |
- *
- * version=0x10(UPnP1.0) data=usn:uuid:1122de4e-8574-59ab-9322-33345678
- * 9044::urn:schemas-upnp-org:service:ContentDirectory:2,usn:uuid:6859d
- * ede-8574-59ab-9332-123456789012::upnp:rootdevice
- *
- * P2P-SERV-DISC-RESP 58:17:0c:bc:dd:ca 21 1900010200045f6970
- * 70c00c000c01094d795072696e746572c027
- * length=25, type=1(Bonjour),transaction id=2,status=0
- *
- * Bonjour Protocol format is as follows.
- * __________________________________________________________
- * |DNS Name(Variable)|DNS Type(1)|Version(1)|RDATA(Variable)|
- *
- * DNS Name=_ipp._tcp.local.,DNS type=12(PTR), Version=1,
- * RDATA=MyPrinter._ipp._tcp.local.
- *
- */
- private static final String P2P_SERV_DISC_RESP_STR = "P2P-SERV-DISC-RESP";
-
- private static final String HOST_AP_EVENT_PREFIX_STR = "AP";
- /* AP-STA-CONNECTED 42:fc:89:a8:96:09 dev_addr=02:90:4c:a0:92:54 */
- private static final String AP_STA_CONNECTED_STR = "AP-STA-CONNECTED";
- /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 */
- private static final String AP_STA_DISCONNECTED_STR = "AP-STA-DISCONNECTED";
- private static final String ANQP_DONE_STR = "ANQP-QUERY-DONE";
- private static final String HS20_ICON_STR = "RX-HS20-ICON";
-
/* Supplicant events reported to a state machine */
private static final int BASE = Protocol.BASE_WIFI_MONITOR;
@@ -462,13 +72,6 @@
public static final int WPS_OVERLAP_EVENT = BASE + 10;
/* WPS timeout detected */
public static final int WPS_TIMEOUT_EVENT = BASE + 11;
- /* Driver was hung */
- public static final int DRIVER_HUNG_EVENT = BASE + 12;
- /* SSID was disabled due to auth failure or excessive
- * connection failures */
- public static final int SSID_TEMP_DISABLED = BASE + 13;
- /* SSID reenabled by supplicant */
- public static final int SSID_REENABLED = BASE + 14;
/* Request Identity */
public static final int SUP_REQUEST_IDENTITY = BASE + 15;
@@ -477,30 +80,9 @@
public static final int SUP_REQUEST_SIM_AUTH = BASE + 16;
public static final int SCAN_FAILED_EVENT = BASE + 17;
+ /* Pno scan results are available */
+ public static final int PNO_SCAN_RESULTS_EVENT = BASE + 18;
- /* P2P events */
- public static final int P2P_DEVICE_FOUND_EVENT = BASE + 21;
- public static final int P2P_DEVICE_LOST_EVENT = BASE + 22;
- public static final int P2P_GO_NEGOTIATION_REQUEST_EVENT = BASE + 23;
- public static final int P2P_GO_NEGOTIATION_SUCCESS_EVENT = BASE + 25;
- public static final int P2P_GO_NEGOTIATION_FAILURE_EVENT = BASE + 26;
- public static final int P2P_GROUP_FORMATION_SUCCESS_EVENT = BASE + 27;
- public static final int P2P_GROUP_FORMATION_FAILURE_EVENT = BASE + 28;
- public static final int P2P_GROUP_STARTED_EVENT = BASE + 29;
- public static final int P2P_GROUP_REMOVED_EVENT = BASE + 30;
- public static final int P2P_INVITATION_RECEIVED_EVENT = BASE + 31;
- public static final int P2P_INVITATION_RESULT_EVENT = BASE + 32;
- public static final int P2P_PROV_DISC_PBC_REQ_EVENT = BASE + 33;
- public static final int P2P_PROV_DISC_PBC_RSP_EVENT = BASE + 34;
- public static final int P2P_PROV_DISC_ENTER_PIN_EVENT = BASE + 35;
- public static final int P2P_PROV_DISC_SHOW_PIN_EVENT = BASE + 36;
- public static final int P2P_FIND_STOPPED_EVENT = BASE + 37;
- public static final int P2P_SERV_DISC_RESP_EVENT = BASE + 38;
- public static final int P2P_PROV_DISC_FAILURE_EVENT = BASE + 39;
-
- /* hostap events */
- public static final int AP_STA_DISCONNECTED_EVENT = BASE + 41;
- public static final int AP_STA_CONNECTED_EVENT = BASE + 42;
/* Indicates assoc reject event */
public static final int ASSOCIATION_REJECTION_EVENT = BASE + 43;
@@ -514,39 +96,30 @@
/* hotspot 2.0 events */
public static final int HS20_REMEDIATION_EVENT = BASE + 61;
- /**
- * This indicates a read error on the monitor socket conenction
- */
- private static final String WPA_RECV_ERROR_STR = "recv error";
+ /* WPS config errrors */
+ private static final int CONFIG_MULTIPLE_PBC_DETECTED = 12;
+ private static final int CONFIG_AUTH_FAILURE = 18;
- /**
- * Max errors before we close supplicant connection
- */
- private static final int MAX_RECV_ERRORS = 10;
+ /* WPS error indications */
+ private static final int REASON_TKIP_ONLY_PROHIBITED = 1;
+ private static final int REASON_WEP_PROHIBITED = 2;
- // Singleton instance
- private static WifiMonitor sWifiMonitor = new WifiMonitor();
- public static WifiMonitor getInstance() {
- return sWifiMonitor;
+ private final WifiInjector mWifiInjector;
+ private boolean mVerboseLoggingEnabled = false;
+ private boolean mConnected = false;
+
+ public WifiMonitor(WifiInjector wifiInjector) {
+ mWifiInjector = wifiInjector;
}
- private final WifiNative mWifiNative;
- private WifiMonitor() {
- mWifiNative = WifiNative.getWlanNativeInterface();
- }
-
- private int mRecvErrors = 0;
-
void enableVerboseLogging(int verbose) {
if (verbose > 0) {
- DBG = true;
+ mVerboseLoggingEnabled = true;
} else {
- DBG = false;
+ mVerboseLoggingEnabled = false;
}
}
- private boolean mConnected = false;
-
// TODO(b/27569474) remove support for multiple handlers for the same event
private final Map<String, SparseArray<Set<Handler>>> mHandlerMap = new HashMap<>();
public synchronized void registerHandler(String iface, int what, Handler handler) {
@@ -568,38 +141,47 @@
Boolean val = mMonitoringMap.get(iface);
if (val == null) {
return false;
- }
- else {
+ } else {
return val.booleanValue();
}
}
- private void setMonitoring(String iface, boolean enabled) {
+ /**
+ * Enable/Disable monitoring for the provided iface.
+ *
+ * @param iface Name of the iface.
+ * @param enabled true to enable, false to disable.
+ */
+ @VisibleForTesting
+ public void setMonitoring(String iface, boolean enabled) {
mMonitoringMap.put(iface, enabled);
}
+
private void setMonitoringNone() {
for (String iface : mMonitoringMap.keySet()) {
setMonitoring(iface, false);
}
}
-
+ /**
+ * Wait for wpa_supplicant's control interface to be ready.
+ *
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
private boolean ensureConnectedLocked() {
if (mConnected) {
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()) {
- mConnected = true;
- new MonitorThread(mWifiNative.getLocalLog()).start();
+ mConnected = mWifiInjector.getWifiNative().connectToSupplicant();
+ if (mConnected) {
return true;
}
- if (connectTries++ < 5) {
+ if (connectTries++ < 50) {
try {
- Thread.sleep(1000);
+ Thread.sleep(100);
} catch (InterruptedException ignore) {
}
} else {
@@ -608,43 +190,44 @@
}
}
- public synchronized void startMonitoring(String iface) {
- Log.d(TAG, "startMonitoring(" + iface + ") with mConnected = " + mConnected);
-
+ /**
+ * Start Monitoring for wpa_supplicant events.
+ *
+ * @param iface Name of iface.
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
+ public synchronized void startMonitoring(String iface, boolean isStaIface) {
if (ensureConnectedLocked()) {
setMonitoring(iface, true);
- sendMessage(iface, SUP_CONNECTION_EVENT);
- }
- else {
+ broadcastSupplicantConnectionEvent(iface);
+ } else {
boolean originalMonitoring = isMonitoring(iface);
setMonitoring(iface, true);
- sendMessage(iface, SUP_DISCONNECTION_EVENT);
+ broadcastSupplicantDisconnectionEvent(iface);
setMonitoring(iface, originalMonitoring);
Log.e(TAG, "startMonitoring(" + iface + ") failed!");
}
}
+ /**
+ * Stop Monitoring for wpa_supplicant events.
+ *
+ * @param iface Name of iface.
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
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);
+ broadcastSupplicantDisconnectionEvent(iface);
setMonitoring(iface, false);
}
- public synchronized void stopSupplicant() {
- mWifiNative.stopSupplicant();
- }
-
- public synchronized void killSupplicant(boolean p2pSupported) {
- String suppState = System.getProperty("init.svc.wpa_supplicant");
- if (suppState == null) suppState = "unknown";
- String p2pSuppState = System.getProperty("init.svc.p2p_supplicant");
- if (p2pSuppState == null) p2pSuppState = "unknown";
-
- Log.e(TAG, "killSupplicant p2p" + p2pSupported
- + " init.svc.wpa_supplicant=" + suppState
- + " init.svc.p2p_supplicant=" + p2pSuppState);
- mWifiNative.killSupplicant(p2pSupported);
+ /**
+ * Stop Monitoring for wpa_supplicant events.
+ *
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
+ public synchronized void stopAllMonitoring() {
mConnected = false;
setMonitoringNone();
}
@@ -679,776 +262,305 @@
SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
if (iface != null && ifaceHandlers != null) {
if (isMonitoring(iface)) {
- boolean firstHandler = true;
Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(message.what);
if (ifaceWhatHandlers != null) {
for (Handler handler : ifaceWhatHandlers) {
- if (firstHandler) {
- firstHandler = false;
- sendMessage(handler, message);
- }
- else {
+ if (handler != null) {
sendMessage(handler, Message.obtain(message));
}
}
}
} 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");
- boolean firstHandler = true;
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Sending to all monitors because there's no matching iface");
+ }
for (Map.Entry<String, SparseArray<Set<Handler>>> entry : mHandlerMap.entrySet()) {
if (isMonitoring(entry.getKey())) {
Set<Handler> ifaceWhatHandlers = entry.getValue().get(message.what);
for (Handler handler : ifaceWhatHandlers) {
- if (firstHandler) {
- firstHandler = false;
- sendMessage(handler, message);
- }
- else {
+ if (handler != null) {
sendMessage(handler, Message.obtain(message));
}
}
}
}
}
+
+ message.recycle();
}
private void sendMessage(Handler handler, Message message) {
- if (handler != null) {
- message.setTarget(handler);
- message.sendToTarget();
- }
- }
-
- private class MonitorThread extends Thread {
- private final LocalLog mLocalLog;
-
- public MonitorThread(LocalLog localLog) {
- super("WifiMonitor");
- mLocalLog = localLog;
- }
-
- public void run() {
- if (DBG) {
- Log.d(TAG, "MonitorThread start with mConnected=" + mConnected);
- }
- //noinspection InfiniteLoopStatement
- for (;;) {
- if (!mConnected) {
- if (DBG) 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 + "]");
- mLocalLog.log("Event [" + eventStr + "]");
- }
-
- if (dispatchEvent(eventStr)) {
- if (DBG) Log.d(TAG, "Disconnecting from the supplicant, no more events");
- break;
- }
- }
- }
- }
-
- private synchronized boolean dispatchEvent(String eventStr) {
- String iface;
- // IFNAME=wlan0 ANQP-QUERY-DONE addr=18:cf:5e:26:a4:88 result=SUCCESS
- if (eventStr.startsWith("IFNAME=")) {
- int space = eventStr.indexOf(' ');
- if (space != -1) {
- iface = eventStr.substring(7, space);
- if (!mHandlerMap.containsKey(iface) && iface.startsWith("p2p-")) {
- // p2p interfaces are created dynamically, but we have
- // only one P2p state machine monitoring all of them; look
- // for it explicitly, and send messages there ..
- iface = "p2p0";
- }
- eventStr = eventStr.substring(space + 1);
- } else {
- // No point dispatching this event to any interface, the dispatched
- // event string will begin with "IFNAME=" which dispatchEvent can't really
- // do anything about.
- Log.e(TAG, "Dropping malformed event (unparsable iface): " + eventStr);
- return false;
- }
- } else {
- // events without prefix belong to p2p0 monitor
- iface = "p2p0";
- }
-
- if (VDBG) Log.d(TAG, "Dispatching event to interface: " + iface);
-
- if (dispatchEvent(eventStr, iface)) {
- mConnected = false;
- return true;
- }
- return false;
- }
-
- private Map<String, Long> mLastConnectBSSIDs = new HashMap<String, Long>() {
- public Long get(String iface) {
- Long value = super.get(iface);
- if (value != null) {
- return value;
- }
- return 0L;
- }
- };
-
- /* @return true if the event was supplicant disconnection */
- private boolean dispatchEvent(String eventStr, String iface) {
- if (DBG) {
- // 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)
- + " dispatchEvent: " + eventStr);
- }
- }
-
- if (!eventStr.startsWith(EVENT_PREFIX_STR)) {
- if (eventStr.startsWith(WPS_SUCCESS_STR)) {
- sendMessage(iface, WPS_SUCCESS_EVENT);
- } else if (eventStr.startsWith(WPS_FAIL_STR)) {
- handleWpsFailEvent(eventStr, iface);
- } else if (eventStr.startsWith(WPS_OVERLAP_STR)) {
- sendMessage(iface, WPS_OVERLAP_EVENT);
- } else if (eventStr.startsWith(WPS_TIMEOUT_STR)) {
- sendMessage(iface, WPS_TIMEOUT_EVENT);
- } else if (eventStr.startsWith(P2P_EVENT_PREFIX_STR)) {
- handleP2pEvents(eventStr, iface);
- } else if (eventStr.startsWith(HOST_AP_EVENT_PREFIX_STR)) {
- handleHostApEvents(eventStr, iface);
- } else if (eventStr.startsWith(ANQP_DONE_STR)) {
- try {
- handleAnqpResult(eventStr, iface);
- }
- catch (IllegalArgumentException iae) {
- Log.e(TAG, "Bad ANQP event string: '" + eventStr + "': " + iae);
- }
- } else if (eventStr.startsWith(HS20_ICON_STR)) {
- try {
- handleIconResult(eventStr, iface);
- }
- catch (IllegalArgumentException iae) {
- Log.e(TAG, "Bad Icon event string: '" + eventStr + "': " + iae);
- }
- }
- else if (eventStr.startsWith(HS20_SUB_REM_STR)) {
- // Tack on the last connected BSSID so we have some idea what AP the WNM pertains to
- handleWnmFrame(String.format("%012x %s",
- mLastConnectBSSIDs.get(iface), eventStr), iface);
- } else if (eventStr.startsWith(HS20_DEAUTH_STR)) {
- handleWnmFrame(String.format("%012x %s",
- mLastConnectBSSIDs.get(iface), eventStr), iface);
- } else if (eventStr.startsWith(REQUEST_PREFIX_STR)) {
- handleRequests(eventStr, iface);
- } else if (eventStr.startsWith(TARGET_BSSID_STR)) {
- handleTargetBSSIDEvent(eventStr, iface);
- } else if (eventStr.startsWith(ASSOCIATED_WITH_STR)) {
- handleAssociatedBSSIDEvent(eventStr, iface);
- } else if (eventStr.startsWith(AUTH_EVENT_PREFIX_STR) &&
- eventStr.endsWith(AUTH_TIMEOUT_STR)) {
- sendMessage(iface, AUTHENTICATION_FAILURE_EVENT);
- } else {
- if (DBG) Log.w(TAG, "couldn't identify event type - " + eventStr);
- }
- eventLogCounter++;
- return false;
- }
-
- String eventName = eventStr.substring(EVENT_PREFIX_LEN_STR);
- int nameEnd = eventName.indexOf(' ');
- 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");
- eventLogCounter++;
- return false;
- }
- /*
- * Map event name into event enum
- */
- int event;
- if (eventName.equals(CONNECTED_STR)) {
- event = CONNECTED;
- long bssid = -1L;
- int prefix = eventStr.indexOf(ConnectPrefix);
- if (prefix >= 0) {
- int suffix = eventStr.indexOf(ConnectSuffix);
- if (suffix > prefix) {
- try {
- bssid = Utils.parseMac(
- eventStr.substring(prefix + ConnectPrefix.length(), suffix));
- } catch (IllegalArgumentException iae) {
- bssid = -1L;
- }
- }
- }
- mLastConnectBSSIDs.put(iface, bssid);
- if (bssid == -1L) {
- Log.w(TAG, "Failed to parse out BSSID from '" + eventStr + "'");
- }
- }
- else if (eventName.equals(DISCONNECTED_STR))
- event = DISCONNECTED;
- else if (eventName.equals(STATE_CHANGE_STR))
- event = STATE_CHANGE;
- else if (eventName.equals(SCAN_RESULTS_STR))
- event = SCAN_RESULTS;
- else if (eventName.equals(SCAN_FAILED_STR))
- event = SCAN_FAILED;
- else if (eventName.equals(LINK_SPEED_STR))
- event = LINK_SPEED;
- else if (eventName.equals(TERMINATING_STR))
- event = TERMINATING;
- else if (eventName.equals(DRIVER_STATE_STR))
- event = DRIVER_STATE;
- else if (eventName.equals(EAP_FAILURE_STR))
- event = EAP_FAILURE;
- else if (eventName.equals(ASSOC_REJECT_STR))
- event = ASSOC_REJECT;
- else if (eventName.equals(TEMP_DISABLED_STR)) {
- event = SSID_TEMP_DISABLE;
- } else if (eventName.equals(REENABLED_STR)) {
- event = SSID_REENABLE;
- } else if (eventName.equals(BSS_ADDED_STR)) {
- event = BSS_ADDED;
- } else if (eventName.equals(BSS_REMOVED_STR)) {
- event = BSS_REMOVED;
- } else
- event = UNKNOWN;
-
- String eventData = eventStr;
- if (event == DRIVER_STATE || event == LINK_SPEED)
- eventData = eventData.split(" ")[1];
- else if (event == STATE_CHANGE || event == EAP_FAILURE) {
- int ind = eventStr.indexOf(" ");
- if (ind != -1) {
- eventData = eventStr.substring(ind + 1);
- }
- } else {
- int ind = eventStr.indexOf(" - ");
- if (ind != -1) {
- eventData = eventStr.substring(ind + 3);
- }
- }
-
- if ((event == SSID_TEMP_DISABLE)||(event == SSID_REENABLE)) {
- String substr = null;
- int netId = -1;
- int ind = eventStr.indexOf(" ");
- if (ind != -1) {
- substr = eventStr.substring(ind + 1);
- }
- if (substr != null) {
- String status[] = substr.split(" ");
- for (String key : status) {
- if (key.regionMatches(0, "id=", 0, 3)) {
- int idx = 3;
- netId = 0;
- while (idx < key.length()) {
- char c = key.charAt(idx);
- if ((c >= 0x30) && (c <= 0x39)) {
- netId *= 10;
- netId += c - 0x30;
- idx++;
- } else {
- break;
- }
- }
- }
- }
- }
- sendMessage(iface, (event == SSID_TEMP_DISABLE)?
- SSID_TEMP_DISABLED:SSID_REENABLED, netId, 0, substr);
- } else if (event == STATE_CHANGE) {
- handleSupplicantStateChange(eventData, iface);
- } else if (event == DRIVER_STATE) {
- handleDriverEvent(eventData, iface);
- } else if (event == TERMINATING) {
- /**
- * Close the supplicant connection if we see
- * too many recv errors
- */
- if (eventData.startsWith(WPA_RECV_ERROR_STR)) {
- if (++mRecvErrors > MAX_RECV_ERRORS) {
- if (DBG) {
- Log.d(TAG, "too many recv errors, closing connection");
- }
- } else {
- eventLogCounter++;
- return false;
- }
- }
-
- // Notify and exit
- sendMessage(null, SUP_DISCONNECTION_EVENT, eventLogCounter);
- return true;
- } else if (event == EAP_FAILURE) {
- if (eventData.startsWith(EAP_AUTH_FAILURE_STR)) {
- sendMessage(iface, AUTHENTICATION_FAILURE_EVENT, eventLogCounter);
- }
- } else if (event == ASSOC_REJECT) {
- Matcher match = mAssocRejectEventPattern.matcher(eventData);
- String BSSID = "";
- int status = -1;
- if (!match.find()) {
- if (DBG) Log.d(TAG, "Assoc Reject: Could not parse assoc reject string");
- } else {
- int groupNumber = match.groupCount();
- int statusGroupNumber = -1;
- if (groupNumber == 2) {
- BSSID = match.group(1);
- statusGroupNumber = 2;
- } else {
- // Under such case Supplicant does not report BSSID
- BSSID = null;
- statusGroupNumber = 1;
- }
- try {
- status = Integer.parseInt(match.group(statusGroupNumber));
- } catch (NumberFormatException e) {
- status = -1;
- }
- }
- sendMessage(iface, ASSOCIATION_REJECTION_EVENT, eventLogCounter, status, BSSID);
- } else if (event == BSS_ADDED && !VDBG) {
- // Ignore that event - it is not handled, and dont log it as it is too verbose
- } else if (event == BSS_REMOVED && !VDBG) {
- // Ignore that event - it is not handled, and dont log it as it is too verbose
- } else {
- handleEvent(event, eventData, iface);
- }
- mRecvErrors = 0;
- eventLogCounter++;
- return false;
- }
-
- private void handleDriverEvent(String state, String iface) {
- if (state == null) {
- return;
- }
- if (state.equals("HANGED")) {
- sendMessage(iface, DRIVER_HUNG_EVENT);
- }
+ message.setTarget(handler);
+ message.sendToTarget();
}
/**
- * Handle all supplicant events except STATE-CHANGE
- * @param event the event type
- * @param remainder the rest of the string following the
- * event name and " — "
+ * Broadcast the WPS fail event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param cfgError Configuration error code.
+ * @param vendorErrorCode Vendor specific error indication code.
*/
- private void handleEvent(int event, String remainder, String iface) {
- if (DBG) {
- Log.d(TAG, "handleEvent " + Integer.toString(event) + " " + remainder);
- }
- switch (event) {
- case DISCONNECTED:
- handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder, iface);
- break;
-
- case CONNECTED:
- handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder, iface);
- break;
-
- case SCAN_RESULTS:
- sendMessage(iface, SCAN_RESULTS_EVENT);
- break;
-
- case SCAN_FAILED:
- sendMessage(iface, SCAN_FAILED_EVENT);
- break;
-
- case UNKNOWN:
- if (DBG) {
- Log.w(TAG, "handleEvent unknown: " + Integer.toString(event) + " " + remainder);
- }
- break;
- default:
- break;
- }
- }
-
- private void handleTargetBSSIDEvent(String eventStr, String iface) {
- String BSSID = null;
- Matcher match = mTargetBSSIDPattern.matcher(eventStr);
- if (match.find()) {
- BSSID = match.group(1);
- }
- sendMessage(iface, WifiStateMachine.CMD_TARGET_BSSID, eventLogCounter, 0, BSSID);
- }
-
- private void handleAssociatedBSSIDEvent(String eventStr, String iface) {
- String BSSID = null;
- Matcher match = mAssociatedPattern.matcher(eventStr);
- if (match.find()) {
- BSSID = match.group(1);
- }
- sendMessage(iface, WifiStateMachine.CMD_ASSOCIATED_BSSID, eventLogCounter, 0, BSSID);
- }
-
-
- private void handleWpsFailEvent(String dataString, String iface) {
- final Pattern p = Pattern.compile(WPS_FAIL_PATTERN);
- Matcher match = p.matcher(dataString);
+ public void broadcastWpsFailEvent(String iface, int cfgError, int vendorErrorCode) {
int reason = 0;
- if (match.find()) {
- String cfgErrStr = match.group(1);
- String reasonStr = match.group(2);
-
- if (reasonStr != null) {
- int reasonInt = Integer.parseInt(reasonStr);
- switch(reasonInt) {
- case REASON_TKIP_ONLY_PROHIBITED:
- sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_TKIP_ONLY_PROHIBITED);
- return;
- case REASON_WEP_PROHIBITED:
- sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_WEP_PROHIBITED);
- return;
- default:
- reason = reasonInt;
- break;
+ switch(vendorErrorCode) {
+ case REASON_TKIP_ONLY_PROHIBITED:
+ sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_TKIP_ONLY_PROHIBITED);
+ return;
+ case REASON_WEP_PROHIBITED:
+ sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_WEP_PROHIBITED);
+ return;
+ default:
+ reason = vendorErrorCode;
+ break;
+ }
+ switch(cfgError) {
+ case CONFIG_AUTH_FAILURE:
+ sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_AUTH_FAILURE);
+ return;
+ case CONFIG_MULTIPLE_PBC_DETECTED:
+ sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_OVERLAP_ERROR);
+ return;
+ default:
+ if (reason == 0) {
+ reason = cfgError;
}
- }
- if (cfgErrStr != null) {
- int cfgErrInt = Integer.parseInt(cfgErrStr);
- switch(cfgErrInt) {
- case CONFIG_AUTH_FAILURE:
- sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_AUTH_FAILURE);
- return;
- case CONFIG_MULTIPLE_PBC_DETECTED:
- sendMessage(iface, WPS_FAIL_EVENT, WifiManager.WPS_OVERLAP_ERROR);
- return;
- default:
- if (reason == 0) reason = cfgErrInt;
- break;
- }
- }
+ break;
}
//For all other errors, return a generic internal error
sendMessage(iface, WPS_FAIL_EVENT, WifiManager.ERROR, reason);
}
- /* <event> status=<err> and the special case of <event> reason=FREQ_CONFLICT */
- private P2pStatus p2pError(String dataString) {
- P2pStatus err = P2pStatus.UNKNOWN;
- String[] tokens = dataString.split(" ");
- if (tokens.length < 2) return err;
- String[] nameValue = tokens[1].split("=");
- if (nameValue.length != 2) return err;
-
- /* Handle the special case of reason=FREQ+CONFLICT */
- if (nameValue[1].equals("FREQ_CONFLICT")) {
- return P2pStatus.NO_COMMON_CHANNEL;
- }
- try {
- err = P2pStatus.valueOf(Integer.parseInt(nameValue[1]));
- } catch (NumberFormatException e) {
- e.printStackTrace();
- }
- return err;
- }
-
- private WifiP2pDevice getWifiP2pDevice(String dataString) {
- try {
- return new WifiP2pDevice(dataString);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
-
- private WifiP2pGroup getWifiP2pGroup(String dataString) {
- try {
- return new WifiP2pGroup(dataString);
- } catch (IllegalArgumentException e) {
- return null;
- }
+ /**
+ * Broadcast the WPS succes event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastWpsSuccessEvent(String iface) {
+ sendMessage(iface, WPS_SUCCESS_EVENT);
}
/**
- * Handle p2p events
+ * Broadcast the WPS overlap event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
*/
- private void handleP2pEvents(String dataString, String iface) {
- if (dataString.startsWith(P2P_DEVICE_FOUND_STR)) {
- WifiP2pDevice device = getWifiP2pDevice(dataString);
- if (device != null) sendMessage(iface, P2P_DEVICE_FOUND_EVENT, device);
- } else if (dataString.startsWith(P2P_DEVICE_LOST_STR)) {
- WifiP2pDevice device = getWifiP2pDevice(dataString);
- if (device != null) sendMessage(iface, P2P_DEVICE_LOST_EVENT, device);
- } else if (dataString.startsWith(P2P_FIND_STOPPED_STR)) {
- sendMessage(iface, P2P_FIND_STOPPED_EVENT);
- } else if (dataString.startsWith(P2P_GO_NEG_REQUEST_STR)) {
- sendMessage(iface, P2P_GO_NEGOTIATION_REQUEST_EVENT, new WifiP2pConfig(dataString));
- } else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) {
- sendMessage(iface, P2P_GO_NEGOTIATION_SUCCESS_EVENT);
- } else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) {
- sendMessage(iface, P2P_GO_NEGOTIATION_FAILURE_EVENT, p2pError(dataString));
- } else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) {
- sendMessage(iface, P2P_GROUP_FORMATION_SUCCESS_EVENT);
- } else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) {
- sendMessage(iface, P2P_GROUP_FORMATION_FAILURE_EVENT, p2pError(dataString));
- } else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) {
- WifiP2pGroup group = getWifiP2pGroup(dataString);
- if (group != null) sendMessage(iface, P2P_GROUP_STARTED_EVENT, group);
- } else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) {
- WifiP2pGroup group = getWifiP2pGroup(dataString);
- if (group != null) sendMessage(iface, P2P_GROUP_REMOVED_EVENT, group);
- } else if (dataString.startsWith(P2P_INVITATION_RECEIVED_STR)) {
- sendMessage(iface, P2P_INVITATION_RECEIVED_EVENT, new WifiP2pGroup(dataString));
- } else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) {
- sendMessage(iface, P2P_INVITATION_RESULT_EVENT, p2pError(dataString));
- } else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
- sendMessage(iface, P2P_PROV_DISC_PBC_REQ_EVENT, new WifiP2pProvDiscEvent(dataString));
- } else if (dataString.startsWith(P2P_PROV_DISC_PBC_RSP_STR)) {
- sendMessage(iface, P2P_PROV_DISC_PBC_RSP_EVENT, new WifiP2pProvDiscEvent(dataString));
- } else if (dataString.startsWith(P2P_PROV_DISC_ENTER_PIN_STR)) {
- sendMessage(iface, P2P_PROV_DISC_ENTER_PIN_EVENT, new WifiP2pProvDiscEvent(dataString));
- } else if (dataString.startsWith(P2P_PROV_DISC_SHOW_PIN_STR)) {
- sendMessage(iface, P2P_PROV_DISC_SHOW_PIN_EVENT, new WifiP2pProvDiscEvent(dataString));
- } else if (dataString.startsWith(P2P_PROV_DISC_FAILURE_STR)) {
- sendMessage(iface, P2P_PROV_DISC_FAILURE_EVENT);
- } else if (dataString.startsWith(P2P_SERV_DISC_RESP_STR)) {
- List<WifiP2pServiceResponse> list = WifiP2pServiceResponse.newInstance(dataString);
- if (list != null) {
- sendMessage(iface, P2P_SERV_DISC_RESP_EVENT, list);
- } else {
- Log.e(TAG, "Null service resp " + dataString);
- }
- }
+ public void broadcastWpsOverlapEvent(String iface) {
+ sendMessage(iface, WPS_OVERLAP_EVENT);
}
/**
- * Handle hostap events
+ * Broadcast the WPS timeout event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
*/
- private void handleHostApEvents(String dataString, String iface) {
- String[] tokens = dataString.split(" ");
- /* AP-STA-CONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=02:90:4c:a0:92:54 */
- if (tokens[0].equals(AP_STA_CONNECTED_STR)) {
- sendMessage(iface, AP_STA_CONNECTED_EVENT, new WifiP2pDevice(dataString));
- /* AP-STA-DISCONNECTED 42:fc:89:a8:96:09 p2p_dev_addr=02:90:4c:a0:92:54 */
- } else if (tokens[0].equals(AP_STA_DISCONNECTED_STR)) {
- sendMessage(iface, AP_STA_DISCONNECTED_EVENT, new WifiP2pDevice(dataString));
- }
- }
-
- private static final String ADDR_STRING = "addr=";
- private static final String RESULT_STRING = "result=";
-
- // ANQP-QUERY-DONE addr=18:cf:5e:26:a4:88 result=SUCCESS
-
- private void handleAnqpResult(String eventStr, String iface) {
- int addrPos = eventStr.indexOf(ADDR_STRING);
- int resPos = eventStr.indexOf(RESULT_STRING);
- if (addrPos < 0 || resPos < 0) {
- throw new IllegalArgumentException("Unexpected ANQP result notification");
- }
- int eoaddr = eventStr.indexOf(' ', addrPos + ADDR_STRING.length());
- if (eoaddr < 0) {
- eoaddr = eventStr.length();
- }
- int eoresult = eventStr.indexOf(' ', resPos + RESULT_STRING.length());
- if (eoresult < 0) {
- eoresult = eventStr.length();
- }
-
- try {
- long bssid = Utils.parseMac(eventStr.substring(addrPos + ADDR_STRING.length(), eoaddr));
- int result = eventStr.substring(
- resPos + RESULT_STRING.length(), eoresult).equalsIgnoreCase("success") ? 1 : 0;
-
- sendMessage(iface, ANQP_DONE_EVENT, result, 0, bssid);
- }
- catch (IllegalArgumentException iae) {
- Log.e(TAG, "Bad MAC address in ANQP response: " + iae.getMessage());
- }
- }
-
- private void handleIconResult(String eventStr, String iface) {
- // RX-HS20-ICON c0:c5:20:27:d1:e8 <file> <size>
- String[] segments = eventStr.split(" ");
- if (segments.length != 4) {
- throw new IllegalArgumentException("Incorrect number of segments");
- }
-
- try {
- String bssid = segments[1];
- String fileName = segments[2];
- int size = Integer.parseInt(segments[3]);
- sendMessage(iface, RX_HS20_ANQP_ICON_EVENT,
- new IconEvent(Utils.parseMac(bssid), fileName, size));
- }
- catch (NumberFormatException nfe) {
- throw new IllegalArgumentException("Bad numeral");
- }
- }
-
- private void handleWnmFrame(String eventStr, String iface) {
- try {
- WnmData wnmData = WnmData.buildWnmData(eventStr);
- sendMessage(iface, HS20_REMEDIATION_EVENT, wnmData);
- } catch (IOException | NumberFormatException e) {
- Log.w(TAG, "Bad WNM event: '" + eventStr + "'");
- }
+ public void broadcastWpsTimeoutEvent(String iface) {
+ sendMessage(iface, WPS_TIMEOUT_EVENT);
}
/**
- * Handle Supplicant Requests
+ * Broadcast the ANQP done event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param anqpEvent ANQP result retrieved.
*/
- private void handleRequests(String dataString, String iface) {
- String SSID = null;
- int reason = -2;
- String requestName = dataString.substring(REQUEST_PREFIX_LEN_STR);
- if (TextUtils.isEmpty(requestName)) {
- return;
- }
- if (requestName.startsWith(IDENTITY_STR)) {
- Matcher match = mRequestIdentityPattern.matcher(requestName);
- if (match.find()) {
- SSID = match.group(2);
- try {
- reason = Integer.parseInt(match.group(1));
- } catch (NumberFormatException e) {
- reason = -1;
- }
- } else {
- Log.e(TAG, "didn't find SSID " + requestName);
- }
- sendMessage(iface, SUP_REQUEST_IDENTITY, eventLogCounter, reason, SSID);
- } else if (requestName.startsWith(SIM_STR)) {
- Matcher matchGsm = mRequestGsmAuthPattern.matcher(requestName);
- Matcher matchUmts = mRequestUmtsAuthPattern.matcher(requestName);
- WifiStateMachine.SimAuthRequestData data =
- new WifiStateMachine.SimAuthRequestData();
- if (matchGsm.find()) {
- data.networkId = Integer.parseInt(matchGsm.group(1));
- data.protocol = WifiEnterpriseConfig.Eap.SIM;
- data.ssid = matchGsm.group(4);
- data.data = matchGsm.group(2).split(":");
- sendMessage(iface, SUP_REQUEST_SIM_AUTH, data);
- } else if (matchUmts.find()) {
- data.networkId = Integer.parseInt(matchUmts.group(1));
- data.protocol = WifiEnterpriseConfig.Eap.AKA;
- data.ssid = matchUmts.group(4);
- data.data = new String[2];
- data.data[0] = matchUmts.group(2);
- data.data[1] = matchUmts.group(3);
- sendMessage(iface, SUP_REQUEST_SIM_AUTH, data);
- } else {
- Log.e(TAG, "couldn't parse SIM auth request - " + requestName);
- }
-
- } else {
- if (DBG) Log.w(TAG, "couldn't identify request type - " + dataString);
- }
+ public void broadcastAnqpDoneEvent(String iface, AnqpEvent anqpEvent) {
+ sendMessage(iface, ANQP_DONE_EVENT, anqpEvent);
}
/**
- * Handle the supplicant STATE-CHANGE event
- * @param dataString New supplicant state string in the format:
- * id=network-id state=new-state
+ * Broadcast the Icon done event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param iconEvent Instance of IconEvent containing the icon data retrieved.
*/
- private void handleSupplicantStateChange(String dataString, String iface) {
- WifiSsid wifiSsid = null;
- int index = dataString.lastIndexOf("SSID=");
- if (index != -1) {
- wifiSsid = WifiSsid.createFromAsciiEncoded(
- dataString.substring(index + 5));
- }
- String[] dataTokens = dataString.split(" ");
-
- String BSSID = null;
- int networkId = -1;
- int newState = -1;
- for (String token : dataTokens) {
- String[] nameValue = token.split("=");
- if (nameValue.length != 2) {
- continue;
- }
-
- if (nameValue[0].equals("BSSID")) {
- BSSID = nameValue[1];
- continue;
- }
-
- int value;
- try {
- value = Integer.parseInt(nameValue[1]);
- } catch (NumberFormatException e) {
- continue;
- }
-
- if (nameValue[0].equals("id")) {
- networkId = value;
- } else if (nameValue[0].equals("state")) {
- newState = value;
- }
- }
-
- if (newState == -1) return;
-
- SupplicantState newSupplicantState = SupplicantState.INVALID;
- for (SupplicantState state : SupplicantState.values()) {
- if (state.ordinal() == newState) {
- newSupplicantState = state;
- break;
- }
- }
- if (newSupplicantState == SupplicantState.INVALID) {
- Log.w(TAG, "Invalid supplicant state: " + newState);
- }
- sendMessage(iface, SUPPLICANT_STATE_CHANGE_EVENT, eventLogCounter, 0,
- new StateChangeResult(networkId, wifiSsid, BSSID, newSupplicantState));
+ public void broadcastIconDoneEvent(String iface, IconEvent iconEvent) {
+ sendMessage(iface, RX_HS20_ANQP_ICON_EVENT, iconEvent);
}
- private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data,
- String iface) {
- String BSSID = null;
- int networkId = -1;
- int reason = 0;
- int ind = -1;
- int local = 0;
- Matcher match;
- if (newState == NetworkInfo.DetailedState.CONNECTED) {
- match = mConnectedEventPattern.matcher(data);
- if (!match.find()) {
- if (DBG) Log.d(TAG, "handleNetworkStateChange: Couldnt find BSSID in event string");
- } else {
- BSSID = match.group(1);
- try {
- networkId = Integer.parseInt(match.group(2));
- } catch (NumberFormatException e) {
- networkId = -1;
- }
- }
- sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, reason, BSSID);
- } else if (newState == NetworkInfo.DetailedState.DISCONNECTED) {
- match = mDisconnectedEventPattern.matcher(data);
- if (!match.find()) {
- if (DBG) Log.d(TAG, "handleNetworkStateChange: Could not parse disconnect string");
- } else {
- BSSID = match.group(1);
- try {
- reason = Integer.parseInt(match.group(2));
- } catch (NumberFormatException e) {
- reason = -1;
- }
- try {
- local = Integer.parseInt(match.group(3));
- } catch (NumberFormatException e) {
- local = -1;
- }
- }
- if (DBG) Log.d(TAG, "WifiMonitor notify network disconnect: "
- + BSSID
- + " reason=" + Integer.toString(reason));
- sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, BSSID);
- }
+ /**
+ * Broadcast the WNM event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param wnmData Instance of WnmData containing the event data.
+ */
+ public void broadcastWnmEvent(String iface, WnmData wnmData) {
+ sendMessage(iface, HS20_REMEDIATION_EVENT, wnmData);
+ }
+
+ /**
+ * Broadcast the Network identity request event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param networkId ID of the network in wpa_supplicant.
+ * @param ssid SSID of the network.
+ */
+ public void broadcastNetworkIdentityRequestEvent(String iface, int networkId, String ssid) {
+ sendMessage(iface, SUP_REQUEST_IDENTITY, 0, networkId, ssid);
+ }
+
+ /**
+ * Broadcast the Network Gsm Sim auth request event to all the handlers registered for this
+ * event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param networkId ID of the network in wpa_supplicant.
+ * @param ssid SSID of the network.
+ * @param data Accompanying event data.
+ */
+ public void broadcastNetworkGsmAuthRequestEvent(String iface, int networkId, String ssid,
+ String[] data) {
+ sendMessage(iface, SUP_REQUEST_SIM_AUTH,
+ new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.SIM, ssid, data));
+ }
+
+ /**
+ * Broadcast the Network Umts Sim auth request event to all the handlers registered for this
+ * event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param networkId ID of the network in wpa_supplicant.
+ * @param ssid SSID of the network.
+ * @param data Accompanying event data.
+ */
+ public void broadcastNetworkUmtsAuthRequestEvent(String iface, int networkId, String ssid,
+ String[] data) {
+ sendMessage(iface, SUP_REQUEST_SIM_AUTH,
+ new SimAuthRequestData(networkId, WifiEnterpriseConfig.Eap.AKA, ssid, data));
+ }
+
+ /**
+ * Broadcast scan result event to all the handlers registered for this event.
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastScanResultEvent(String iface) {
+ sendMessage(iface, SCAN_RESULTS_EVENT);
+ }
+
+ /**
+ * Broadcast pno scan result event to all the handlers registered for this event.
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastPnoScanResultEvent(String iface) {
+ sendMessage(iface, PNO_SCAN_RESULTS_EVENT);
+ }
+
+ /**
+ * Broadcast scan failed event to all the handlers registered for this event.
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastScanFailedEvent(String iface) {
+ sendMessage(iface, SCAN_FAILED_EVENT);
+ }
+
+ /**
+ * Broadcast the authentication failure event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param reason Reason for authentication failure. This has to be one of the
+ * {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_NONE},
+ * {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_TIMEOUT},
+ * {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_WRONG_PSWD},
+ * {@link android.net.wifi.WifiManager#ERROR_AUTH_FAILURE_EAP_FAILURE}
+ */
+ public void broadcastAuthenticationFailureEvent(String iface, int reason) {
+ sendMessage(iface, AUTHENTICATION_FAILURE_EVENT, 0, reason);
+ }
+
+ /**
+ * Broadcast the association rejection event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param status Status code for association rejection.
+ * @param timedOut Indicates if the association timed out.
+ * @param bssid BSSID of the access point from which we received the reject.
+ */
+ public void broadcastAssociationRejectionEvent(String iface, int status, boolean timedOut,
+ String bssid) {
+ sendMessage(iface, ASSOCIATION_REJECTION_EVENT, timedOut ? 1 : 0, status, bssid);
+ }
+
+ /**
+ * Broadcast the association success event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param bssid BSSID of the access point.
+ */
+ public void broadcastAssociatedBssidEvent(String iface, String bssid) {
+ sendMessage(iface, WifiStateMachine.CMD_ASSOCIATED_BSSID, 0, 0, bssid);
+ }
+
+ /**
+ * Broadcast the start of association event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param bssid BSSID of the access point.
+ */
+ public void broadcastTargetBssidEvent(String iface, String bssid) {
+ sendMessage(iface, WifiStateMachine.CMD_TARGET_BSSID, 0, 0, bssid);
+ }
+
+ /**
+ * Broadcast the network connection event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param networkId ID of the network in wpa_supplicant.
+ * @param bssid BSSID of the access point.
+ */
+ public void broadcastNetworkConnectionEvent(String iface, int networkId, String bssid) {
+ sendMessage(iface, NETWORK_CONNECTION_EVENT, networkId, 0, bssid);
+ }
+
+ /**
+ * Broadcast the network disconnection event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param local Whether the disconnect was locally triggered.
+ * @param reason Disconnect reason code.
+ * @param bssid BSSID of the access point.
+ */
+ public void broadcastNetworkDisconnectionEvent(String iface, int local, int reason,
+ String bssid) {
+ sendMessage(iface, NETWORK_DISCONNECTION_EVENT, local, reason, bssid);
+ }
+
+ /**
+ * Broadcast the supplicant state change event to all the handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param networkId ID of the network in wpa_supplicant.
+ * @param bssid BSSID of the access point.
+ * @param newSupplicantState New supplicant state.
+ */
+ public void broadcastSupplicantStateChangeEvent(String iface, int networkId, WifiSsid wifiSsid,
+ String bssid,
+ SupplicantState newSupplicantState) {
+ sendMessage(iface, SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+ new StateChangeResult(networkId, wifiSsid, bssid, newSupplicantState));
+ }
+
+ /**
+ * Broadcast the connection to wpa_supplicant event to all the handlers registered for
+ * this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastSupplicantConnectionEvent(String iface) {
+ sendMessage(iface, SUP_CONNECTION_EVENT);
+ }
+
+ /**
+ * Broadcast the loss of connection to wpa_supplicant event to all the handlers registered for
+ * this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastSupplicantDisconnectionEvent(String iface) {
+ sendMessage(iface, SUP_DISCONNECTION_EVENT);
}
}
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 13876f3..90f6ac1 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -16,54 +16,28 @@
package com.android.server.wifi;
-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.IApInterface;
+import android.net.wifi.IClientInterface;
import android.net.wifi.RttManager;
import android.net.wifi.RttManager.ResponderConfig;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiLinkLayerStats;
-import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
-import android.net.wifi.WifiSsid;
import android.net.wifi.WifiWakeReasonAndCounts;
-import android.net.wifi.WpsInfo;
-import android.net.wifi.p2p.WifiP2pConfig;
-import android.net.wifi.p2p.WifiP2pGroup;
-import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.LocalLog;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.Immutable;
import com.android.internal.util.HexDump;
import com.android.server.connectivity.KeepalivePacketData;
-import com.android.server.wifi.hotspot2.NetworkDetail;
-import com.android.server.wifi.hotspot2.SupplicantBridge;
-import com.android.server.wifi.hotspot2.Utils;
import com.android.server.wifi.util.FrameParser;
-import com.android.server.wifi.util.InformationElementUtil;
-
-import libcore.util.HexEncoding;
-
-import org.json.JSONException;
-import org.json.JSONObject;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
@@ -71,12 +45,7 @@
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.BitSet;
import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -87,703 +56,264 @@
* Native calls for bring up/shut down of the supplicant daemon and for
* sending requests to the supplicant daemon
*
- * waitForEvent() is called on the monitor thread for events. All other methods
- * must be serialized from the framework.
- *
* {@hide}
*/
public class WifiNative {
- private static boolean DBG = false;
-
- // Must match wifi_hal.h
- public static final int WIFI_SUCCESS = 0;
-
- /**
- * Hold this lock before calling supplicant or HAL methods
- * it is required to mutually exclude access to the driver
- */
- public static final Object sLock = new Object();
-
- private static final LocalLog sLocalLog = new LocalLog(8192);
-
- public @NonNull LocalLog getLocalLog() {
- return sLocalLog;
- }
-
- /* Register native functions */
- static {
- /* Native functions are defined in libwifi-service.so */
- System.loadLibrary("wifi-service");
- registerNatives();
- }
-
- private static native int registerNatives();
-
- /*
- * Singleton WifiNative instances
- */
- private static WifiNative wlanNativeInterface =
- new WifiNative(SystemProperties.get("wifi.interface", "wlan0"), true);
- public static WifiNative getWlanNativeInterface() {
- return wlanNativeInterface;
- }
-
- private static WifiNative p2pNativeInterface =
- // commands for p2p0 interface don't need prefix
- new WifiNative(SystemProperties.get("wifi.direct.interface", "p2p0"), false);
- public static WifiNative getP2pNativeInterface() {
- return p2pNativeInterface;
- }
-
-
private final String mTAG;
private final String mInterfaceName;
- private final String mInterfacePrefix;
+ private final SupplicantStaIfaceHal mSupplicantStaIfaceHal;
+ private final WifiVendorHal mWifiVendorHal;
+ private final WificondControl mWificondControl;
- private Context mContext = null;
- public void initContext(Context context) {
- if (mContext == null && context != null) {
- mContext = context;
- }
- }
-
- private WifiNative(String interfaceName,
- boolean requiresPrefix) {
- mInterfaceName = interfaceName;
+ public WifiNative(String interfaceName, WifiVendorHal vendorHal,
+ SupplicantStaIfaceHal staIfaceHal, WificondControl condControl) {
mTAG = "WifiNative-" + interfaceName;
-
- if (requiresPrefix) {
- mInterfacePrefix = "IFNAME=" + interfaceName + " ";
- } else {
- mInterfacePrefix = "";
- }
+ mInterfaceName = interfaceName;
+ mWifiVendorHal = vendorHal;
+ mSupplicantStaIfaceHal = staIfaceHal;
+ mWificondControl = condControl;
}
public String getInterfaceName() {
return mInterfaceName;
}
- // Note this affects logging on for all interfaces
- void enableVerboseLogging(int verbose) {
- if (verbose > 0) {
- DBG = true;
- } else {
- DBG = false;
- }
- }
-
- private void localLog(String s) {
- if (sLocalLog != null) sLocalLog.log(mInterfaceName + ": " + s);
- }
-
-
-
- /*
- * Driver and Supplicant management
+ /**
+ * Enable verbose logging for all sub modules.
*/
- private native static boolean loadDriverNative();
- public boolean loadDriver() {
- synchronized (sLock) {
- return loadDriverNative();
- }
+ public void enableVerboseLogging(int verbose) {
+ mWificondControl.enableVerboseLogging(verbose > 0 ? true : false);
+ mSupplicantStaIfaceHal.enableVerboseLogging(verbose > 0);
+ mWifiVendorHal.enableVerboseLogging(verbose > 0);
}
- private native static boolean isDriverLoadedNative();
- public boolean isDriverLoaded() {
- synchronized (sLock) {
- return isDriverLoadedNative();
- }
- }
+ /********************************************************
+ * Native Initialization/Deinitialization
+ ********************************************************/
- private native static boolean unloadDriverNative();
- public boolean unloadDriver() {
- synchronized (sLock) {
- return unloadDriverNative();
+ /**
+ * Setup wifi native for Client mode operations.
+ *
+ * 1. Starts the Wifi HAL and configures it in client/STA mode.
+ * 2. Setup Wificond to operate in client mode and retrieve the handle to use for client
+ * operations.
+ *
+ * @return An IClientInterface as wificond client interface binder handler.
+ * Returns null on failure.
+ */
+ public IClientInterface setupForClientMode() {
+ if (!startHalIfNecessary(true)) {
+ Log.e(mTAG, "Failed to start HAL for client mode");
+ return null;
}
- }
-
- private native static boolean startSupplicantNative(boolean p2pSupported);
- public boolean startSupplicant(boolean p2pSupported) {
- synchronized (sLock) {
- return startSupplicantNative(p2pSupported);
- }
- }
-
- /* Sends a kill signal to supplicant. To be used when we have lost connection
- or when the supplicant is hung */
- private native static boolean killSupplicantNative(boolean p2pSupported);
- public boolean killSupplicant(boolean p2pSupported) {
- synchronized (sLock) {
- return killSupplicantNative(p2pSupported);
- }
- }
-
- private native static boolean connectToSupplicantNative();
- public boolean connectToSupplicant() {
- synchronized (sLock) {
- localLog(mInterfacePrefix + "connectToSupplicant");
- return connectToSupplicantNative();
- }
- }
-
- private native static void closeSupplicantConnectionNative();
- public void closeSupplicantConnection() {
- synchronized (sLock) {
- localLog(mInterfacePrefix + "closeSupplicantConnection");
- closeSupplicantConnectionNative();
- }
+ return mWificondControl.setupDriverForClientMode();
}
/**
- * Wait for the supplicant to send an event, returning the event string.
- * @return the event string sent by the supplicant.
+ * Setup wifi native for AP mode operations.
+ *
+ * 1. Starts the Wifi HAL and configures it in AP mode.
+ * 2. Setup Wificond to operate in AP mode and retrieve the handle to use for ap operations.
+ *
+ * @return An IApInterface as wificond Ap interface binder handler.
+ * Returns null on failure.
*/
- private native static String waitForEventNative();
- public String waitForEvent() {
- // No synchronization necessary .. it is implemented in WifiMonitor
- return waitForEventNative();
- }
-
-
- /*
- * Supplicant Command Primitives
- */
- private native boolean doBooleanCommandNative(String command);
-
- private native int doIntCommandNative(String command);
-
- private native String doStringCommandNative(String command);
-
- private boolean doBooleanCommand(String command) {
- if (DBG) Log.d(mTAG, "doBoolean: " + command);
- synchronized (sLock) {
- String toLog = mInterfacePrefix + command;
- boolean result = doBooleanCommandNative(mInterfacePrefix + command);
- localLog(toLog + " -> " + result);
- if (DBG) Log.d(mTAG, command + ": returned " + result);
- return result;
+ public IApInterface setupForSoftApMode() {
+ if (!startHalIfNecessary(false)) {
+ Log.e(mTAG, "Failed to start HAL for AP mode");
+ return null;
}
- }
-
- private boolean doBooleanCommandWithoutLogging(String command) {
- if (DBG) Log.d(mTAG, "doBooleanCommandWithoutLogging: " + command);
- synchronized (sLock) {
- boolean result = doBooleanCommandNative(mInterfacePrefix + command);
- if (DBG) Log.d(mTAG, command + ": returned " + result);
- return result;
- }
- }
-
- private int doIntCommand(String command) {
- if (DBG) Log.d(mTAG, "doInt: " + command);
- synchronized (sLock) {
- String toLog = mInterfacePrefix + command;
- int result = doIntCommandNative(mInterfacePrefix + command);
- localLog(toLog + " -> " + result);
- if (DBG) Log.d(mTAG, " returned " + result);
- return result;
- }
- }
-
- private String doStringCommand(String command) {
- if (DBG) {
- //GET_NETWORK commands flood the logs
- if (!command.startsWith("GET_NETWORK")) {
- Log.d(mTAG, "doString: [" + command + "]");
- }
- }
- synchronized (sLock) {
- String toLog = mInterfacePrefix + command;
- String result = doStringCommandNative(mInterfacePrefix + command);
- if (result == null) {
- if (DBG) Log.d(mTAG, "doStringCommandNative no result");
- } else {
- if (!command.startsWith("STATUS-")) {
- localLog(toLog + " -> " + result);
- }
- if (DBG) Log.d(mTAG, " returned " + result.replace("\n", " "));
- }
- return result;
- }
- }
-
- private String doStringCommandWithoutLogging(String command) {
- if (DBG) {
- //GET_NETWORK commands flood the logs
- if (!command.startsWith("GET_NETWORK")) {
- Log.d(mTAG, "doString: [" + command + "]");
- }
- }
- synchronized (sLock) {
- return doStringCommandNative(mInterfacePrefix + command);
- }
- }
-
- public String doCustomSupplicantCommand(String command) {
- return doStringCommand(command);
- }
-
- /*
- * Wrappers for supplicant commands
- */
- public boolean ping() {
- String pong = doStringCommand("PING");
- return (pong != null && pong.equals("PONG"));
- }
-
- public void setSupplicantLogLevel(String level) {
- doStringCommand("LOG_LEVEL " + level);
- }
-
- public String getFreqCapability() {
- return doStringCommand("GET_CAPABILITY freq");
+ return mWificondControl.setupDriverForSoftApMode();
}
/**
- * Create a comma separate string from integer set.
- * @param values List of integers.
- * @return comma separated string.
+ * Teardown all mode configurations in wifi native.
+ *
+ * 1. Tears down all the interfaces from Wificond.
+ * 2. Stops the Wifi HAL.
+ *
+ * @return Returns true on success.
*/
- 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;
+ public boolean tearDown() {
+ if (!mWificondControl.tearDownInterfaces()) {
+ // TODO(b/34859006): Handle failures.
+ Log.e(mTAG, "Failed to teardown interfaces from Wificond");
+ return false;
}
- return list.toString();
+ stopHalIfNecessary();
+ return true;
+ }
+
+ /********************************************************
+ * Wificond operations
+ ********************************************************/
+ /**
+ * Result of a signal poll.
+ */
+ public static class SignalPollResult {
+ // RSSI value in dBM.
+ public int currentRssi;
+ //Transmission bit rate in Mbps.
+ public int txBitrate;
+ // Association frequency in MHz.
+ public int associationFrequency;
}
/**
- * Start a scan using wpa_supplicant for the given frequencies.
+ * WiFi interface transimission counters.
+ */
+ public static class TxPacketCounters {
+ // Number of successfully transmitted packets.
+ public int txSucceeded;
+ // Number of tramsmission failures.
+ public int txFailed;
+ }
+
+ /**
+ * Disable wpa_supplicant via wificond.
+ * @return Returns true on success.
+ */
+ public boolean disableSupplicant() {
+ return mWificondControl.disableSupplicant();
+ }
+
+ /**
+ * Enable wpa_supplicant via wificond.
+ * @return Returns true on success.
+ */
+ public boolean enableSupplicant() {
+ return mWificondControl.enableSupplicant();
+ }
+
+ /**
+ * Request signal polling to wificond.
+ * Returns an SignalPollResult object.
+ * Returns null on failure.
+ */
+ public SignalPollResult signalPoll() {
+ return mWificondControl.signalPoll();
+ }
+
+ /**
+ * Fetch TX packet counters on current connection from wificond.
+ * Returns an TxPacketCounters object.
+ * Returns null on failure.
+ */
+ public TxPacketCounters getTxPacketCounters() {
+ return mWificondControl.getTxPacketCounters();
+ }
+
+ /**
+ * Start a scan using wificond for the given parameters.
* @param freqs list of frequencies to scan for, if null scan all supported channels.
- * @param hiddenNetworkIds List of hidden networks to be scanned for.
+ * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
+ * @return Returns true on success.
*/
- public boolean scan(Set<Integer> freqs, Set<Integer> hiddenNetworkIds) {
- String freqList = null;
- String hiddenNetworkIdList = null;
- if (freqs != null && freqs.size() != 0) {
- freqList = createCSVStringFromIntegerSet(freqs);
- }
- if (hiddenNetworkIds != null && hiddenNetworkIds.size() != 0) {
- hiddenNetworkIdList = createCSVStringFromIntegerSet(hiddenNetworkIds);
- }
- return scanWithParams(freqList, hiddenNetworkIdList);
- }
-
- private boolean scanWithParams(String freqList, String hiddenNetworkIdList) {
- StringBuilder scanCommand = new StringBuilder();
- scanCommand.append("SCAN TYPE=ONLY");
- if (freqList != null) {
- scanCommand.append(" freq=" + freqList);
- }
- if (hiddenNetworkIdList != null) {
- scanCommand.append(" scan_id=" + hiddenNetworkIdList);
- }
- return doBooleanCommand(scanCommand.toString());
- }
-
- /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
- *
- * Note that underneath we use a harsh-sounding "terminate" supplicant command
- * for a graceful stop and a mild-sounding "stop" interface
- * to kill the process
- */
- public boolean stopSupplicant() {
- return doBooleanCommand("TERMINATE");
- }
-
- public String listNetworks() {
- return doStringCommand("LIST_NETWORKS");
- }
-
- public String listNetworks(int last_id) {
- return doStringCommand("LIST_NETWORKS LAST_ID=" + last_id);
- }
-
- public int addNetwork() {
- return doIntCommand("ADD_NETWORK");
- }
-
- public boolean setNetworkExtra(int netId, String name, 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;
- } catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
- return false;
- }
- return setNetworkVariable(netId, name, "\"" + encoded + "\"");
- }
-
- public boolean setNetworkVariable(int netId, String name, String value) {
- if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
- if (name.equals(WifiConfiguration.pskVarName)
- || name.equals(WifiEnterpriseConfig.PASSWORD_KEY)) {
- return doBooleanCommandWithoutLogging("SET_NETWORK " + netId + " " + name + " " + value);
- } else {
- return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
- }
- }
-
- public Map<String, String> getNetworkExtra(int netId, String name) {
- final String wrapped = getNetworkVariable(netId, name);
- if (wrapped == null || !wrapped.startsWith("\"") || !wrapped.endsWith("\"")) {
- return null;
- }
- try {
- final String encoded = wrapped.substring(1, wrapped.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
- // constructor will thrown a JSONException and the method will return null.
- final JSONObject json = new JSONObject(URLDecoder.decode(encoded, "UTF-8"));
- final Map<String, String> values = new HashMap<String, String>();
- final Iterator<?> it = json.keys();
- while (it.hasNext()) {
- final String key = (String) it.next();
- final Object value = json.get(key);
- if (value instanceof String) {
- values.put(key, (String) value);
- }
- }
- return values;
- } catch (UnsupportedEncodingException e) {
- Log.e(TAG, "Unable to deserialize networkExtra: " + e.toString());
- return null;
- } catch (JSONException e) {
- // This is not necessarily an error. This exception will also occur if we encounter a
- // legacy FQDN stored as a plain string. We want to return null in this case as no JSON
- // dictionary of extras was found.
- return null;
- }
- }
-
- public String getNetworkVariable(int netId, String name) {
- if (TextUtils.isEmpty(name)) return null;
-
- // GET_NETWORK will likely flood the logs ...
- return doStringCommandWithoutLogging("GET_NETWORK " + netId + " " + name);
- }
-
- public boolean removeNetwork(int netId) {
- return doBooleanCommand("REMOVE_NETWORK " + netId);
- }
-
-
- private void logDbg(String debug) {
- long now = SystemClock.elapsedRealtimeNanos();
- String ts = String.format("[%,d us] ", now/1000);
- Log.e("WifiNative: ", ts+debug+ " stack:"
- + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "
- + Thread.currentThread().getStackTrace()[3].getMethodName() +" - "
- + Thread.currentThread().getStackTrace()[4].getMethodName() +" - "
- + Thread.currentThread().getStackTrace()[5].getMethodName()+" - "
- + Thread.currentThread().getStackTrace()[6].getMethodName());
-
+ public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {
+ return mWificondControl.scan(freqs, hiddenNetworkSSIDs);
}
/**
- * Enables a network in wpa_supplicant.
- * @param netId - Network ID of the network to be enabled.
- * @return true if command succeeded, false otherwise.
+ * Fetch the latest scan result from kernel via wificond.
+ * @return Returns an ArrayList of ScanDetail.
+ * Returns an empty ArrayList on failure.
*/
- public boolean enableNetwork(int netId) {
- if (DBG) logDbg("enableNetwork nid=" + Integer.toString(netId));
- return doBooleanCommand("ENABLE_NETWORK " + netId);
- }
-
- /**
- * Enable a network in wpa_supplicant, do not connect.
- * @param netId - Network ID of the network to be enabled.
- * @return true if command succeeded, false otherwise.
- */
- public boolean enableNetworkWithoutConnect(int netId) {
- if (DBG) logDbg("enableNetworkWithoutConnect nid=" + Integer.toString(netId));
- return doBooleanCommand("ENABLE_NETWORK " + netId + " " + "no-connect");
- }
-
- /**
- * Disables a network in wpa_supplicant.
- * @param netId - Network ID of the network to be disabled.
- * @return true if command succeeded, false otherwise.
- */
- public boolean disableNetwork(int netId) {
- if (DBG) logDbg("disableNetwork nid=" + Integer.toString(netId));
- return doBooleanCommand("DISABLE_NETWORK " + netId);
- }
-
- /**
- * Select a network in wpa_supplicant (Disables all others).
- * @param netId - Network ID of the network to be selected.
- * @return true if command succeeded, false otherwise.
- */
- public boolean selectNetwork(int netId) {
- if (DBG) logDbg("selectNetwork nid=" + Integer.toString(netId));
- return doBooleanCommand("SELECT_NETWORK " + netId);
- }
-
- public boolean reconnect() {
- if (DBG) logDbg("RECONNECT ");
- return doBooleanCommand("RECONNECT");
- }
-
- public boolean reassociate() {
- if (DBG) logDbg("REASSOCIATE ");
- return doBooleanCommand("REASSOCIATE");
- }
-
- public boolean disconnect() {
- if (DBG) logDbg("DISCONNECT ");
- return doBooleanCommand("DISCONNECT");
- }
-
- public String status() {
- return status(false);
- }
-
- public String status(boolean noEvents) {
- if (noEvents) {
- return doStringCommand("STATUS-NO_EVENTS");
- } else {
- return doStringCommand("STATUS");
- }
- }
-
- public String getMacAddress() {
- //Macaddr = XX.XX.XX.XX.XX.XX
- String ret = doStringCommand("DRIVER MACADDR");
- if (!TextUtils.isEmpty(ret)) {
- String[] tokens = ret.split(" = ");
- if (tokens.length == 2) return tokens[1];
- }
- return null;
- }
-
-
-
- /**
- * Format of results:
- * =================
- * id=1
- * bssid=68:7f:76:d7:1a:6e
- * freq=2412
- * level=-44
- * tsf=1344626243700342
- * flags=[WPA2-PSK-CCMP][WPS][ESS]
- * ssid=zfdy
- * ====
- * id=2
- * bssid=68:5f:74:d7:1a:6f
- * freq=5180
- * level=-73
- * tsf=1344626243700373
- * flags=[WPA2-PSK-CCMP][WPS][ESS]
- * ssid=zuby
- * ====
- *
- * RANGE=ALL gets all scan results
- * RANGE=ID- gets results from ID
- * MASK=<N> BSS command information mask.
- *
- * The mask used in this method, 0x29d87, gets the following fields:
- *
- * WPA_BSS_MASK_ID (Bit 0)
- * WPA_BSS_MASK_BSSID (Bit 1)
- * WPA_BSS_MASK_FREQ (Bit 2)
- * WPA_BSS_MASK_LEVEL (Bit 7)
- * WPA_BSS_MASK_TSF (Bit 8)
- * WPA_BSS_MASK_IE (Bit 10)
- * WPA_BSS_MASK_FLAGS (Bit 11)
- * WPA_BSS_MASK_SSID (Bit 12)
- * WPA_BSS_MASK_INTERNETW (Bit 15) (adds ANQP info)
- * WPA_BSS_MASK_DELIM (Bit 17)
- *
- * See wpa_supplicant/src/common/wpa_ctrl.h for details.
- */
- private String getRawScanResults(String range) {
- return doStringCommandWithoutLogging("BSS RANGE=" + range + " MASK=0x29d87");
- }
-
- private static final String BSS_IE_STR = "ie=";
- private static final String BSS_ID_STR = "id=";
- private static final String BSS_BSSID_STR = "bssid=";
- private static final String BSS_FREQ_STR = "freq=";
- private static final String BSS_LEVEL_STR = "level=";
- private static final String BSS_TSF_STR = "tsf=";
- private static final String BSS_FLAGS_STR = "flags=";
- private static final String BSS_SSID_STR = "ssid=";
- private static final String BSS_DELIMITER_STR = "====";
- private static final String BSS_END_STR = "####";
-
public ArrayList<ScanDetail> getScanResults() {
- int next_sid = 0;
- ArrayList<ScanDetail> results = new ArrayList<>();
- while(next_sid >= 0) {
- String rawResult = getRawScanResults(next_sid+"-");
- next_sid = -1;
-
- if (TextUtils.isEmpty(rawResult))
- break;
-
- String[] lines = rawResult.split("\n");
-
-
- // note that all these splits and substrings keep references to the original
- // huge string buffer while the amount we really want is generally pretty small
- // so make copies instead (one example b/11087956 wasted 400k of heap here).
- final int bssidStrLen = BSS_BSSID_STR.length();
- final int flagLen = BSS_FLAGS_STR.length();
-
- String bssid = "";
- int level = 0;
- int freq = 0;
- long tsf = 0;
- String flags = "";
- WifiSsid wifiSsid = null;
- String infoElementsStr = null;
- List<String> anqpLines = null;
-
- for (String line : lines) {
- if (line.startsWith(BSS_ID_STR)) { // Will find the last id line
- try {
- next_sid = Integer.parseInt(line.substring(BSS_ID_STR.length())) + 1;
- } catch (NumberFormatException e) {
- // Nothing to do
- }
- } else if (line.startsWith(BSS_BSSID_STR)) {
- bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
- } else if (line.startsWith(BSS_FREQ_STR)) {
- try {
- freq = Integer.parseInt(line.substring(BSS_FREQ_STR.length()));
- } catch (NumberFormatException e) {
- freq = 0;
- }
- } else if (line.startsWith(BSS_LEVEL_STR)) {
- try {
- level = Integer.parseInt(line.substring(BSS_LEVEL_STR.length()));
- /* some implementations avoid negative values by adding 256
- * so we need to adjust for that here.
- */
- if (level > 0) level -= 256;
- } catch (NumberFormatException e) {
- level = 0;
- }
- } else if (line.startsWith(BSS_TSF_STR)) {
- try {
- tsf = Long.parseLong(line.substring(BSS_TSF_STR.length()));
- } catch (NumberFormatException e) {
- tsf = 0;
- }
- } else if (line.startsWith(BSS_FLAGS_STR)) {
- flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
- } else if (line.startsWith(BSS_SSID_STR)) {
- wifiSsid = WifiSsid.createFromAsciiEncoded(
- line.substring(BSS_SSID_STR.length()));
- } else if (line.startsWith(BSS_IE_STR)) {
- infoElementsStr = line;
- } else if (SupplicantBridge.isAnqpAttribute(line)) {
- if (anqpLines == null) {
- anqpLines = new ArrayList<>();
- }
- anqpLines.add(line);
- } else if (line.startsWith(BSS_DELIMITER_STR) || line.startsWith(BSS_END_STR)) {
- if (bssid != null) {
- try {
- if (infoElementsStr == null) {
- throw new IllegalArgumentException("Null information element data");
- }
- int seperator = infoElementsStr.indexOf('=');
- if (seperator < 0) {
- throw new IllegalArgumentException("No element separator");
- }
-
- ScanResult.InformationElement[] infoElements =
- InformationElementUtil.parseInformationElements(
- Utils.hexToBytes(infoElementsStr.substring(seperator + 1)));
-
- NetworkDetail networkDetail = new NetworkDetail(bssid,
- infoElements, anqpLines, freq);
- String xssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
- if (!xssid.equals(networkDetail.getTrimmedSSID())) {
- Log.d(TAG, String.format(
- "Inconsistent SSID on BSSID '%s': '%s' vs '%s': %s",
- bssid, xssid, networkDetail.getSSID(), infoElementsStr));
- }
-
- if (networkDetail.hasInterworking()) {
- if (DBG) Log.d(TAG, "HSNwk: '" + networkDetail);
- }
- ScanDetail scan = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
- level, freq, tsf, infoElements, anqpLines);
- results.add(scan);
- } catch (IllegalArgumentException iae) {
- Log.d(TAG, "Failed to parse information elements: " + iae);
- }
- }
- bssid = null;
- level = 0;
- freq = 0;
- tsf = 0;
- flags = "";
- wifiSsid = null;
- infoElementsStr = null;
- anqpLines = null;
- }
- }
- }
- return results;
+ return mWificondControl.getScanResults();
}
/**
- * Format of result:
- * id=1016
- * bssid=00:03:7f:40:84:10
- * freq=2462
- * beacon_int=200
- * capabilities=0x0431
- * qual=0
- * noise=0
- * level=-46
- * tsf=0000002669008476
- * age=5
- * ie=00105143412d485332302d52322d54455354010882848b960c12182403010b0706555...
- * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
- * ssid=QCA-HS20-R2-TEST
- * p2p_device_name=
- * p2p_config_methods=0x0SET_NE
- * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
- * anqp_network_auth_type=010000
- * anqp_roaming_consortium=03506f9a05001bc504bd
- * anqp_ip_addr_type_availability=0c
- * anqp_nai_realm=0200300000246d61696c2e6578616d706c652e636f6d3b636973636f2...
- * anqp_3gpp=000600040132f465
- * anqp_domain_name=0b65786d61706c652e636f6d
- * hs20_operator_friendly_name=11656e6757692d466920416c6c69616e63650e636869...
- * hs20_wan_metrics=01c40900008001000000000a00
- * hs20_connection_capability=0100000006140001061600000650000106bb010106bb0...
- * hs20_osu_providers_list=0b5143412d4f53552d425353010901310015656e6757692d...
+ * Start PNO scan.
+ * @param pnoSettings Pno scan configuration.
+ * @return true on success.
*/
- public String scanResult(String bssid) {
- return doStringCommand("BSS " + bssid);
+ public boolean startPnoScan(PnoSettings pnoSettings) {
+ return mWificondControl.startPnoScan(pnoSettings);
}
- public boolean startDriver() {
- return doBooleanCommand("DRIVER START");
+ /**
+ * Stop PNO scan.
+ * @return true on success.
+ */
+ public boolean stopPnoScan() {
+ return mWificondControl.stopPnoScan();
}
- public boolean stopDriver() {
- return doBooleanCommand("DRIVER STOP");
+ /********************************************************
+ * Supplicant operations
+ ********************************************************/
+
+ /**
+ * This method is called repeatedly until the connection to wpa_supplicant is established.
+ *
+ * @return true if connection is established, false otherwise.
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
+ public boolean connectToSupplicant() {
+ // Start initialization if not already started.
+ if (!mSupplicantStaIfaceHal.isInitializationStarted()
+ && !mSupplicantStaIfaceHal.initialize()) {
+ return false;
+ }
+ // Check if the initialization is complete.
+ return mSupplicantStaIfaceHal.isInitializationComplete();
}
+ /**
+ * Close supplicant connection.
+ */
+ public void closeSupplicantConnection() {
+ // Nothing to do for HIDL.
+ }
/**
+ * Set supplicant log level
+ *
+ * @param turnOnVerbose Whether to turn on verbose logging or not.
+ */
+ public void setSupplicantLogLevel(boolean turnOnVerbose) {
+ mSupplicantStaIfaceHal.setLogLevel(turnOnVerbose);
+ }
+
+ /**
+ * Trigger a reconnection if the iface is disconnected.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean reconnect() {
+ return mSupplicantStaIfaceHal.reconnect();
+ }
+
+ /**
+ * Trigger a reassociation even if the iface is currently connected.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean reassociate() {
+ return mSupplicantStaIfaceHal.reassociate();
+ }
+
+ /**
+ * Trigger a disconnection from the currently connected network.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean disconnect() {
+ return mSupplicantStaIfaceHal.disconnect();
+ }
+
+ /**
+ * Makes a callback to HIDL to getMacAddress from supplicant
+ *
+ * @return string containing the MAC address, or null on a failed call
+ */
+ public String getMacAddress() {
+ return mSupplicantStaIfaceHal.getMacAddress();
+ }
+
+ public static final int RX_FILTER_TYPE_V4_MULTICAST = 0;
+ public static final int RX_FILTER_TYPE_V6_MULTICAST = 1;
+ /**
* Start filtering out Multicast V4 packets
* @return {@code true} if the operation succeeded, {@code false} otherwise
*
@@ -808,9 +338,10 @@
* The SETSUSPENDOPT driver command overrides the filtering rules
*/
public boolean startFilteringMulticastV4Packets() {
- return doBooleanCommand("DRIVER RXFILTER-STOP")
- && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
- && doBooleanCommand("DRIVER RXFILTER-START");
+ return mSupplicantStaIfaceHal.stopRxFilter()
+ && mSupplicantStaIfaceHal.removeRxFilter(
+ RX_FILTER_TYPE_V4_MULTICAST)
+ && mSupplicantStaIfaceHal.startRxFilter();
}
/**
@@ -818,9 +349,10 @@
* @return {@code true} if the operation succeeded, {@code false} otherwise
*/
public boolean stopFilteringMulticastV4Packets() {
- return doBooleanCommand("DRIVER RXFILTER-STOP")
- && doBooleanCommand("DRIVER RXFILTER-ADD 2")
- && doBooleanCommand("DRIVER RXFILTER-START");
+ return mSupplicantStaIfaceHal.stopRxFilter()
+ && mSupplicantStaIfaceHal.addRxFilter(
+ RX_FILTER_TYPE_V4_MULTICAST)
+ && mSupplicantStaIfaceHal.startRxFilter();
}
/**
@@ -828,9 +360,10 @@
* @return {@code true} if the operation succeeded, {@code false} otherwise
*/
public boolean startFilteringMulticastV6Packets() {
- return doBooleanCommand("DRIVER RXFILTER-STOP")
- && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
- && doBooleanCommand("DRIVER RXFILTER-START");
+ return mSupplicantStaIfaceHal.stopRxFilter()
+ && mSupplicantStaIfaceHal.removeRxFilter(
+ RX_FILTER_TYPE_V6_MULTICAST)
+ && mSupplicantStaIfaceHal.startRxFilter();
}
/**
@@ -838,34 +371,15 @@
* @return {@code true} if the operation succeeded, {@code false} otherwise
*/
public boolean stopFilteringMulticastV6Packets() {
- return doBooleanCommand("DRIVER RXFILTER-STOP")
- && doBooleanCommand("DRIVER RXFILTER-ADD 3")
- && doBooleanCommand("DRIVER RXFILTER-START");
+ return mSupplicantStaIfaceHal.stopRxFilter()
+ && mSupplicantStaIfaceHal.addRxFilter(
+ RX_FILTER_TYPE_V6_MULTICAST)
+ && mSupplicantStaIfaceHal.startRxFilter();
}
- /**
- * Set the operational frequency band
- * @param band One of
- * {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
- * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
- * {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
- * @return {@code true} if the operation succeeded, {@code false} otherwise
- */
- public boolean setBand(int band) {
- String bandstr;
-
- if (band == WifiManager.WIFI_FREQUENCY_BAND_5GHZ)
- bandstr = "5G";
- else if (band == WifiManager.WIFI_FREQUENCY_BAND_2GHZ)
- bandstr = "2G";
- else
- bandstr = "AUTO";
- return doBooleanCommand("SET SETBAND " + bandstr);
- }
-
- public static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
- public static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1;
- public static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2;
+ public static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
+ public static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1;
+ public static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2;
/**
* Sets the bluetooth coexistence mode.
*
@@ -875,7 +389,7 @@
* @return Whether the mode was successfully set.
*/
public boolean setBluetoothCoexistenceMode(int mode) {
- return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
+ return mSupplicantStaIfaceHal.setBtCoexistenceMode(mode);
}
/**
@@ -883,758 +397,456 @@
* 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) {
- if (setCoexScanMode) {
- return doBooleanCommand("DRIVER BTCOEXSCAN-START");
- } else {
- return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
- }
- }
-
- public void enableSaveConfig() {
- doBooleanCommand("SET update_config 1");
- }
-
- public boolean saveConfig() {
- return doBooleanCommand("SAVE_CONFIG");
- }
-
- public boolean addToBlacklist(String bssid) {
- if (TextUtils.isEmpty(bssid)) return false;
- return doBooleanCommand("BLACKLIST " + bssid);
- }
-
- public boolean clearBlacklist() {
- return doBooleanCommand("BLACKLIST clear");
- }
-
- public boolean setSuspendOptimizations(boolean enabled) {
- if (enabled) {
- return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
- } else {
- return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
- }
- }
-
- public boolean setCountryCode(String countryCode) {
- if (countryCode != null)
- return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
- else
- return doBooleanCommand("DRIVER COUNTRY");
+ return mSupplicantStaIfaceHal.setBtCoexistenceScanModeEnabled(setCoexScanMode);
}
/**
- * Start/Stop PNO scan.
- * @param enable boolean indicating whether PNO is being enabled or disabled.
+ * Enable or disable suspend mode optimizations.
+ *
+ * @param enabled true to enable, false otherwise.
+ * @return true if request is sent successfully, false otherwise.
*/
- public boolean setPnoScan(boolean enable) {
- String cmd = enable ? "SET pno 1" : "SET pno 0";
- return doBooleanCommand(cmd);
+ public boolean setSuspendOptimizations(boolean enabled) {
+ return mSupplicantStaIfaceHal.setSuspendModeEnabled(enabled);
}
- public void enableAutoConnect(boolean enable) {
- if (enable) {
- doBooleanCommand("STA_AUTOCONNECT 1");
- } else {
- doBooleanCommand("STA_AUTOCONNECT 0");
- }
+ /**
+ * Set country code.
+ *
+ * @param countryCode 2 byte ASCII string. For ex: US, CA.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setCountryCode(String countryCode) {
+ return mSupplicantStaIfaceHal.setCountryCode(countryCode);
}
- public void setScanInterval(int scanInterval) {
- doBooleanCommand("SCAN_INTERVAL " + scanInterval);
- }
-
- public void setHs20(boolean hs20) {
- if (hs20) {
- doBooleanCommand("SET HS20 1");
- } else {
- doBooleanCommand("SET HS20 0");
- }
- }
-
+ /**
+ * Initiate TDLS discover and setup or teardown with the specified peer.
+ *
+ * @param macAddr MAC Address of the peer.
+ * @param enable true to start discovery and setup, false to teardown.
+ */
public void startTdls(String macAddr, boolean enable) {
if (enable) {
- synchronized (sLock) {
- doBooleanCommand("TDLS_DISCOVER " + macAddr);
- doBooleanCommand("TDLS_SETUP " + macAddr);
- }
+ mSupplicantStaIfaceHal.initiateTdlsDiscover(macAddr);
+ mSupplicantStaIfaceHal.initiateTdlsSetup(macAddr);
} else {
- doBooleanCommand("TDLS_TEARDOWN " + macAddr);
+ mSupplicantStaIfaceHal.initiateTdlsTeardown(macAddr);
}
}
- /** Example output:
- * RSSI=-65
- * LINKSPEED=48
- * NOISE=9999
- * FREQUENCY=0
- */
- public String signalPoll() {
- return doStringCommandWithoutLogging("SIGNAL_POLL");
- }
-
- /** Example outout:
- * TXGOOD=396
- * TXBAD=1
- */
- public String pktcntPoll() {
- return doStringCommand("PKTCNT_POLL");
- }
-
- public void bssFlush() {
- doBooleanCommand("BSS_FLUSH 0");
- }
-
- public boolean startWpsPbc(String bssid) {
- if (TextUtils.isEmpty(bssid)) {
- return doBooleanCommand("WPS_PBC");
- } else {
- return doBooleanCommand("WPS_PBC " + bssid);
- }
- }
-
- public boolean startWpsPbc(String iface, String bssid) {
- synchronized (sLock) {
- if (TextUtils.isEmpty(bssid)) {
- return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
- } else {
- return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
- }
- }
- }
-
- public boolean startWpsPinKeypad(String pin) {
- if (TextUtils.isEmpty(pin)) return false;
- return doBooleanCommand("WPS_PIN any " + pin);
- }
-
- public boolean startWpsPinKeypad(String iface, String pin) {
- if (TextUtils.isEmpty(pin)) return false;
- synchronized (sLock) {
- return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
- }
- }
-
-
- public String startWpsPinDisplay(String bssid) {
- if (TextUtils.isEmpty(bssid)) {
- return doStringCommand("WPS_PIN any");
- } else {
- return doStringCommand("WPS_PIN " + bssid);
- }
- }
-
- public String startWpsPinDisplay(String iface, String bssid) {
- synchronized (sLock) {
- if (TextUtils.isEmpty(bssid)) {
- return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
- } else {
- return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
- }
- }
- }
-
- public boolean setExternalSim(boolean external) {
- String value = external ? "1" : "0";
- Log.d(TAG, "Setting external_sim to " + value);
- return doBooleanCommand("SET external_sim " + value);
- }
-
- public boolean simAuthResponse(int id, String type, String response) {
- // with type = GSM-AUTH, UMTS-AUTH or UMTS-AUTS
- return doBooleanCommand("CTRL-RSP-SIM-" + id + ":" + type + response);
- }
-
- public boolean simAuthFailedResponse(int id) {
- // should be used with type GSM-AUTH
- return doBooleanCommand("CTRL-RSP-SIM-" + id + ":GSM-FAIL");
- }
-
- public boolean umtsAuthFailedResponse(int id) {
- // should be used with type UMTS-AUTH
- return doBooleanCommand("CTRL-RSP-SIM-" + id + ":UMTS-FAIL");
- }
-
- public boolean simIdentityResponse(int id, String response) {
- return doBooleanCommand("CTRL-RSP-IDENTITY-" + id + ":" + response);
- }
-
- /* Configures an access point connection */
- public boolean startWpsRegistrar(String bssid, String pin) {
- if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
- return doBooleanCommand("WPS_REG " + bssid + " " + pin);
- }
-
- public boolean cancelWps() {
- return doBooleanCommand("WPS_CANCEL");
- }
-
- public boolean setPersistentReconnect(boolean enabled) {
- int value = (enabled == true) ? 1 : 0;
- return doBooleanCommand("SET persistent_reconnect " + value);
- }
-
- public boolean setDeviceName(String name) {
- return doBooleanCommand("SET device_name " + name);
- }
-
- public boolean setDeviceType(String type) {
- return doBooleanCommand("SET device_type " + type);
- }
-
- public boolean setConfigMethods(String cfg) {
- return doBooleanCommand("SET config_methods " + cfg);
- }
-
- public boolean setManufacturer(String value) {
- return doBooleanCommand("SET manufacturer " + value);
- }
-
- public boolean setModelName(String value) {
- return doBooleanCommand("SET model_name " + value);
- }
-
- public boolean setModelNumber(String value) {
- return doBooleanCommand("SET model_number " + value);
- }
-
- public boolean setSerialNumber(String value) {
- return doBooleanCommand("SET serial_number " + value);
- }
-
- public boolean setP2pSsidPostfix(String postfix) {
- return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
- }
-
- public boolean setP2pGroupIdle(String iface, int time) {
- synchronized (sLock) {
- return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
- }
- }
-
- public void setPowerSave(boolean enabled) {
- if (enabled) {
- doBooleanCommand("SET ps 1");
- } else {
- doBooleanCommand("SET ps 0");
- }
- }
-
- public boolean setP2pPowerSave(String iface, boolean enabled) {
- synchronized (sLock) {
- if (enabled) {
- return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
- } else {
- return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
- }
- }
- }
-
- public boolean setWfdEnable(boolean enable) {
- return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
- }
-
- public boolean setWfdDeviceInfo(String hex) {
- return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
- }
-
/**
- * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
- * P2P connection over STA
+ * Start WPS pin display operation with the specified peer.
+ *
+ * @param bssid BSSID of the peer.
+ * @return true if request is sent successfully, false otherwise.
*/
- public boolean setConcurrencyPriority(String s) {
- return doBooleanCommand("P2P_SET conc_pref " + s);
+ public boolean startWpsPbc(String bssid) {
+ return mSupplicantStaIfaceHal.startWpsPbc(bssid);
}
- public boolean p2pFind() {
- return doBooleanCommand("P2P_FIND");
+ /**
+ * Start WPS pin keypad operation with the specified pin.
+ *
+ * @param pin Pin to be used.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean startWpsPinKeypad(String pin) {
+ return mSupplicantStaIfaceHal.startWpsPinKeypad(pin);
}
- public boolean p2pFind(int timeout) {
- if (timeout <= 0) {
- return p2pFind();
- }
- return doBooleanCommand("P2P_FIND " + timeout);
+ /**
+ * Start WPS pin display operation with the specified peer.
+ *
+ * @param bssid BSSID of the peer.
+ * @return new pin generated on success, null otherwise.
+ */
+ public String startWpsPinDisplay(String bssid) {
+ return mSupplicantStaIfaceHal.startWpsPinDisplay(bssid);
}
- public boolean p2pStopFind() {
- return doBooleanCommand("P2P_STOP_FIND");
+ /**
+ * Sets whether to use external sim for SIM/USIM processing.
+ *
+ * @param external true to enable, false otherwise.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setExternalSim(boolean external) {
+ return mSupplicantStaIfaceHal.setExternalSim(external);
}
- public boolean p2pListen() {
- return doBooleanCommand("P2P_LISTEN");
- }
+ /**
+ * Sim auth response types.
+ */
+ public static final String SIM_AUTH_RESP_TYPE_GSM_AUTH = "GSM-AUTH";
+ public static final String SIM_AUTH_RESP_TYPE_UMTS_AUTH = "UMTS-AUTH";
+ public static final String SIM_AUTH_RESP_TYPE_UMTS_AUTS = "UMTS-AUTS";
- public boolean p2pListen(int timeout) {
- if (timeout <= 0) {
- return p2pListen();
- }
- return doBooleanCommand("P2P_LISTEN " + timeout);
- }
-
- public boolean p2pExtListen(boolean enable, int period, int interval) {
- if (enable && interval < period) {
+ /**
+ * Send the sim auth response for the currently configured network.
+ *
+ * @param type |GSM-AUTH|, |UMTS-AUTH| or |UMTS-AUTS|.
+ * @param response Response params.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean simAuthResponse(int id, String type, String response) {
+ if (SIM_AUTH_RESP_TYPE_GSM_AUTH.equals(type)) {
+ return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthResponse(response);
+ } else if (SIM_AUTH_RESP_TYPE_UMTS_AUTH.equals(type)) {
+ return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthResponse(response);
+ } else if (SIM_AUTH_RESP_TYPE_UMTS_AUTS.equals(type)) {
+ return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAutsResponse(response);
+ } else {
return false;
}
- return doBooleanCommand("P2P_EXT_LISTEN"
- + (enable ? (" " + period + " " + interval) : ""));
}
- public boolean p2pSetChannel(int lc, int oc) {
- if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
-
- synchronized (sLock) {
- if (lc >=1 && lc <= 11) {
- if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
- return false;
- }
- } else if (lc != 0) {
- return false;
- }
-
- if (oc >= 1 && oc <= 165 ) {
- int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
- return doBooleanCommand("P2P_SET disallow_freq 1000-"
- + (freq - 5) + "," + (freq + 5) + "-6000");
- } else if (oc == 0) {
- /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
- return doBooleanCommand("P2P_SET disallow_freq \"\"");
- }
- }
- return false;
- }
-
- public boolean p2pFlush() {
- return doBooleanCommand("P2P_FLUSH");
- }
-
- private static final int DEFAULT_GROUP_OWNER_INTENT = 6;
- /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
- [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
- public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
- if (config == null) return null;
- List<String> args = new ArrayList<String>();
- WpsInfo wps = config.wps;
- args.add(config.deviceAddress);
-
- switch (wps.setup) {
- case WpsInfo.PBC:
- args.add("pbc");
- break;
- case WpsInfo.DISPLAY:
- if (TextUtils.isEmpty(wps.pin)) {
- args.add("pin");
- } else {
- args.add(wps.pin);
- }
- args.add("display");
- break;
- case WpsInfo.KEYPAD:
- args.add(wps.pin);
- args.add("keypad");
- break;
- case WpsInfo.LABEL:
- args.add(wps.pin);
- args.add("label");
- default:
- break;
- }
-
- if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
- args.add("persistent");
- }
-
- if (joinExistingGroup) {
- args.add("join");
- } else {
- //TODO: This can be adapted based on device plugged in state and
- //device battery state
- int groupOwnerIntent = config.groupOwnerIntent;
- if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
- groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
- }
- args.add("go_intent=" + groupOwnerIntent);
- }
-
- String command = "P2P_CONNECT ";
- for (String s : args) command += s + " ";
-
- return doStringCommand(command);
- }
-
- public boolean p2pCancelConnect() {
- return doBooleanCommand("P2P_CANCEL");
- }
-
- public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
- if (config == null) return false;
-
- switch (config.wps.setup) {
- case WpsInfo.PBC:
- return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
- case WpsInfo.DISPLAY:
- //We are doing display, so provision discovery is keypad
- return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
- case WpsInfo.KEYPAD:
- //We are doing keypad, so provision discovery is display
- return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
- default:
- break;
- }
- return false;
- }
-
- public boolean p2pGroupAdd(boolean persistent) {
- if (persistent) {
- return doBooleanCommand("P2P_GROUP_ADD persistent");
- }
- return doBooleanCommand("P2P_GROUP_ADD");
- }
-
- public boolean p2pGroupAdd(int netId) {
- return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
- }
-
- public boolean p2pGroupRemove(String iface) {
- if (TextUtils.isEmpty(iface)) return false;
- synchronized (sLock) {
- return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
- }
- }
-
- public boolean p2pReject(String deviceAddress) {
- return doBooleanCommand("P2P_REJECT " + deviceAddress);
- }
-
- /* Invite a peer to a group */
- public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
- if (TextUtils.isEmpty(deviceAddress)) return false;
-
- if (group == null) {
- return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
- } else {
- return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
- + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
- }
- }
-
- /* Reinvoke a persistent connection */
- public boolean p2pReinvoke(int netId, String deviceAddress) {
- if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
-
- return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
- }
-
- public String p2pGetSsid(String deviceAddress) {
- return p2pGetParam(deviceAddress, "oper_ssid");
- }
-
- public String p2pGetDeviceAddress() {
- Log.d(TAG, "p2pGetDeviceAddress");
-
- String status = null;
-
- /* Explicitly calling the API without IFNAME= prefix to take care of the devices that
- don't have p2p0 interface. Supplicant seems to be returning the correct address anyway. */
-
- synchronized (sLock) {
- status = doStringCommandNative("STATUS");
- }
-
- String result = "";
- if (status != null) {
- String[] tokens = status.split("\n");
- for (String token : tokens) {
- if (token.startsWith("p2p_device_address=")) {
- String[] nameValue = token.split("=");
- if (nameValue.length != 2)
- break;
- result = nameValue[1];
- }
- }
- }
-
- Log.d(TAG, "p2pGetDeviceAddress returning " + result);
- return result;
- }
-
- public int getGroupCapability(String deviceAddress) {
- int gc = 0;
- if (TextUtils.isEmpty(deviceAddress)) return gc;
- String peerInfo = p2pPeer(deviceAddress);
- if (TextUtils.isEmpty(peerInfo)) return gc;
-
- String[] tokens = peerInfo.split("\n");
- for (String token : tokens) {
- if (token.startsWith("group_capab=")) {
- String[] nameValue = token.split("=");
- if (nameValue.length != 2) break;
- try {
- return Integer.decode(nameValue[1]);
- } catch(NumberFormatException e) {
- return gc;
- }
- }
- }
- return gc;
- }
-
- public String p2pPeer(String deviceAddress) {
- return doStringCommand("P2P_PEER " + deviceAddress);
- }
-
- private String p2pGetParam(String deviceAddress, String key) {
- if (deviceAddress == null) return null;
-
- String peerInfo = p2pPeer(deviceAddress);
- if (peerInfo == null) return null;
- String[] tokens= peerInfo.split("\n");
-
- key += "=";
- for (String token : tokens) {
- if (token.startsWith(key)) {
- String[] nameValue = token.split("=");
- if (nameValue.length != 2) break;
- return nameValue[1];
- }
- }
- return null;
- }
-
- public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
- /*
- * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
- * P2P_SERVICE_ADD upnp <version hex> <service>
- *
- * e.g)
- * [Bonjour]
- * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
- * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
- * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
- * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
- * 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
- *
- * [UPnP]
- * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
- * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
- * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
- * -org:device:InternetGatewayDevice:1
- * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
- * -org:service:ContentDirectory:2
- */
- synchronized (sLock) {
- for (String s : servInfo.getSupplicantQueryList()) {
- String command = "P2P_SERVICE_ADD";
- command += (" " + s);
- if (!doBooleanCommand(command)) {
- return false;
- }
- }
- }
- return true;
- }
-
- public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
- /*
- * P2P_SERVICE_DEL bonjour <query hexdump>
- * P2P_SERVICE_DEL upnp <version hex> <service>
- */
- synchronized (sLock) {
- for (String s : servInfo.getSupplicantQueryList()) {
- String command = "P2P_SERVICE_DEL ";
-
- String[] data = s.split(" ");
- if (data.length < 2) {
- return false;
- }
- if ("upnp".equals(data[0])) {
- command += s;
- } else if ("bonjour".equals(data[0])) {
- command += data[0];
- command += (" " + data[1]);
- } else {
- return false;
- }
- if (!doBooleanCommand(command)) {
- return false;
- }
- }
- }
- return true;
- }
-
- public boolean p2pServiceFlush() {
- return doBooleanCommand("P2P_SERVICE_FLUSH");
- }
-
- public String p2pServDiscReq(String addr, String query) {
- String command = "P2P_SERV_DISC_REQ";
- command += (" " + addr);
- command += (" " + query);
-
- return doStringCommand(command);
- }
-
- public boolean p2pServDiscCancelReq(String id) {
- return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
- }
-
- /* Set the current mode of miracast operation.
- * 0 = disabled
- * 1 = operating as source
- * 2 = operating as sink
+ /**
+ * Send the eap sim gsm auth failure for the currently configured network.
+ *
+ * @return true if succeeds, false otherwise.
*/
- public void setMiracastMode(int mode) {
- // Note: optional feature on the driver. It is ok for this to fail.
- doBooleanCommand("DRIVER MIRACAST " + mode);
+ public boolean simAuthFailedResponse(int id) {
+ return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthFailure();
}
- public boolean fetchAnqp(String bssid, String subtypes) {
- return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
- }
-
- /*
- * NFC-related calls
+ /**
+ * Send the eap sim umts auth failure for the currently configured network.
+ *
+ * @return true if succeeds, false otherwise.
*/
- public String getNfcWpsConfigurationToken(int netId) {
- return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
+ public boolean umtsAuthFailedResponse(int id) {
+ return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthFailure();
}
- public String getNfcHandoverRequest() {
- return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
+ /**
+ * Send the eap identity response for the currently configured network.
+ *
+ * @param response String to send.
+ * @return true if succeeds, false otherwise.
+ */
+ public boolean simIdentityResponse(int id, String response) {
+ return mSupplicantStaIfaceHal.sendCurrentNetworkEapIdentityResponse(response);
}
- public String getNfcHandoverSelect() {
- return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
+ /**
+ * This get anonymous identity from supplicant and returns it as a string.
+ *
+ * @return anonymous identity string if succeeds, null otherwise.
+ */
+ public String getEapAnonymousIdentity() {
+ return mSupplicantStaIfaceHal.getCurrentNetworkEapAnonymousIdentity();
}
- public boolean initiatorReportNfcHandover(String selectMessage) {
- return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
+ /**
+ * Start WPS pin registrar operation with the specified peer and pin.
+ *
+ * @param bssid BSSID of the peer.
+ * @param pin Pin to be used.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean startWpsRegistrar(String bssid, String pin) {
+ return mSupplicantStaIfaceHal.startWpsRegistrar(bssid, pin);
}
- public boolean responderReportNfcHandover(String requestMessage) {
- return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
+ /**
+ * Cancels any ongoing WPS requests.
+ *
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean cancelWps() {
+ return mSupplicantStaIfaceHal.cancelWps();
}
+ /**
+ * Set WPS device name.
+ *
+ * @param name String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setDeviceName(String name) {
+ return mSupplicantStaIfaceHal.setWpsDeviceName(name);
+ }
- /* kernel logging support */
- private static native byte[] readKernelLogNative();
+ /**
+ * Set WPS device type.
+ *
+ * @param type Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setDeviceType(String type) {
+ return mSupplicantStaIfaceHal.setWpsDeviceType(type);
+ }
- synchronized public String readKernelLog() {
- byte[] bytes = readKernelLogNative();
- if (bytes != null) {
- CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
- try {
- CharBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
- return decoded.toString();
- } catch (CharacterCodingException cce) {
- return new String(bytes, StandardCharsets.ISO_8859_1);
- }
- } else {
- return "*** failed to read kernel log ***";
+ /**
+ * Set WPS config methods
+ *
+ * @param cfg List of config methods.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setConfigMethods(String cfg) {
+ return mSupplicantStaIfaceHal.setWpsConfigMethods(cfg);
+ }
+
+ /**
+ * Set WPS manufacturer.
+ *
+ * @param value String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setManufacturer(String value) {
+ return mSupplicantStaIfaceHal.setWpsManufacturer(value);
+ }
+
+ /**
+ * Set WPS model name.
+ *
+ * @param value String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setModelName(String value) {
+ return mSupplicantStaIfaceHal.setWpsModelName(value);
+ }
+
+ /**
+ * Set WPS model number.
+ *
+ * @param value String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setModelNumber(String value) {
+ return mSupplicantStaIfaceHal.setWpsModelNumber(value);
+ }
+
+ /**
+ * Set WPS serial number.
+ *
+ * @param value String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setSerialNumber(String value) {
+ return mSupplicantStaIfaceHal.setWpsSerialNumber(value);
+ }
+
+ /**
+ * Enable or disable power save mode.
+ *
+ * @param enabled true to enable, false to disable.
+ */
+ public void setPowerSave(boolean enabled) {
+ mSupplicantStaIfaceHal.setPowerSave(enabled);
+ }
+
+ /**
+ * Set concurrency priority between P2P & STA operations.
+ *
+ * @param isStaHigherPriority Set to true to prefer STA over P2P during concurrency operations,
+ * false otherwise.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setConcurrencyPriority(boolean isStaHigherPriority) {
+ return mSupplicantStaIfaceHal.setConcurrencyPriority(isStaHigherPriority);
+ }
+
+ /**
+ * Enable/Disable auto reconnect functionality in wpa_supplicant.
+ *
+ * @param enable true to enable auto reconnecting, false to disable.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean enableStaAutoReconnect(boolean enable) {
+ return mSupplicantStaIfaceHal.enableAutoReconnect(enable);
+ }
+
+ /**
+ * Migrate all the configured networks from wpa_supplicant.
+ *
+ * @param configs Map of configuration key to configuration objects corresponding to all
+ * the networks.
+ * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf
+ * @return Max priority of all the configs.
+ */
+ public boolean migrateNetworksFromSupplicant(Map<String, WifiConfiguration> configs,
+ SparseArray<Map<String, String>> networkExtras) {
+ return mSupplicantStaIfaceHal.loadNetworks(configs, networkExtras);
+ }
+
+ /**
+ * Add the provided network configuration to wpa_supplicant and initiate connection to it.
+ * This method does the following:
+ * 1. Abort any ongoing scan to unblock the connection request.
+ * 2. Remove any existing network in wpa_supplicant(This implicitly triggers disconnect).
+ * 3. Add a new network to wpa_supplicant.
+ * 4. Save the provided configuration to wpa_supplicant.
+ * 5. Select the new network in wpa_supplicant.
+ * 6. Triggers reconnect command to wpa_supplicant.
+ *
+ * @param configuration WifiConfiguration parameters for the provided network.
+ * @return {@code true} if it succeeds, {@code false} otherwise
+ */
+ public boolean connectToNetwork(WifiConfiguration configuration) {
+ // Abort ongoing scan before connect() to unblock connection request.
+ mWificondControl.abortScan();
+ return mSupplicantStaIfaceHal.connectToNetwork(configuration);
+ }
+
+ /**
+ * Initiates roaming to the already configured network in wpa_supplicant. If the network
+ * configuration provided does not match the already configured network, then this triggers
+ * a new connection attempt (instead of roam).
+ * 1. Abort any ongoing scan to unblock the roam request.
+ * 2. First check if we're attempting to connect to the same network as we currently have
+ * configured.
+ * 3. Set the new bssid for the network in wpa_supplicant.
+ * 4. Triggers reassociate command to wpa_supplicant.
+ *
+ * @param configuration WifiConfiguration parameters for the provided network.
+ * @return {@code true} if it succeeds, {@code false} otherwise
+ */
+ public boolean roamToNetwork(WifiConfiguration configuration) {
+ // Abort ongoing scan before connect() to unblock roaming request.
+ mWificondControl.abortScan();
+ return mSupplicantStaIfaceHal.roamToNetwork(configuration);
+ }
+
+ /**
+ * Get the framework network ID corresponding to the provided supplicant network ID for the
+ * network configured in wpa_supplicant.
+ *
+ * @param supplicantNetworkId network ID in wpa_supplicant for the network.
+ * @return Corresponding framework network ID if found, -1 if network not found.
+ */
+ public int getFrameworkNetworkId(int supplicantNetworkId) {
+ return supplicantNetworkId;
+ }
+
+ /**
+ * Remove all the networks.
+ *
+ * @return {@code true} if it succeeds, {@code false} otherwise
+ */
+ public boolean removeAllNetworks() {
+ return mSupplicantStaIfaceHal.removeAllNetworks();
+ }
+
+ /**
+ * Set the BSSID for the currently configured network in wpa_supplicant.
+ *
+ * @return true if successful, false otherwise.
+ */
+ public boolean setConfiguredNetworkBSSID(String bssid) {
+ return mSupplicantStaIfaceHal.setCurrentNetworkBssid(bssid);
+ }
+
+ /**
+ * Initiate ANQP query.
+ *
+ * @param bssid BSSID of the AP to be queried
+ * @param anqpIds Set of anqp IDs.
+ * @param hs20Subtypes Set of HS20 subtypes.
+ * @return true on success, false otherwise.
+ */
+ public boolean requestAnqp(String bssid, Set<Integer> anqpIds, Set<Integer> hs20Subtypes) {
+ if (bssid == null || ((anqpIds == null || anqpIds.isEmpty())
+ && (hs20Subtypes == null || hs20Subtypes.isEmpty()))) {
+ Log.e(mTAG, "Invalid arguments for ANQP request.");
+ return false;
}
- }
-
- /* WIFI HAL support */
-
- // HAL command ids
- private static int sCmdId = 1;
- private static int getNewCmdIdLocked() {
- return sCmdId++;
- }
-
- private static final String TAG = "WifiNative-HAL";
- private static long sWifiHalHandle = 0; /* used by JNI to save wifi_handle */
- private static long[] sWifiIfaceHandles = null; /* used by JNI to save interface handles */
- public static int sWlan0Index = -1;
- private static MonitorThread sThread;
- private static final int STOP_HAL_TIMEOUT_MS = 1000;
-
- private static native boolean startHalNative();
- private static native void stopHalNative();
- private static native void waitForHalEventNative();
-
- private static class MonitorThread extends Thread {
- public void run() {
- Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
- waitForHalEventNative();
+ ArrayList<Short> anqpIdList = new ArrayList<>();
+ for (Integer anqpId : anqpIds) {
+ anqpIdList.add(anqpId.shortValue());
}
+ ArrayList<Integer> hs20SubtypeList = new ArrayList<>();
+ hs20SubtypeList.addAll(hs20Subtypes);
+ return mSupplicantStaIfaceHal.initiateAnqpQuery(bssid, anqpIdList, hs20SubtypeList);
}
- public boolean startHal() {
- String debugLog = "startHal stack: ";
- java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
- for (int i = 2; i < elements.length && i <= 7; i++ ) {
- debugLog = debugLog + " - " + elements[i].getMethodName();
+ /**
+ * Request a passpoint icon file |filename| from the specified AP |bssid|.
+ * @param bssid BSSID of the AP
+ * @param fileName name of the icon file
+ * @return true if request is sent successfully, false otherwise
+ */
+ public boolean requestIcon(String bssid, String fileName) {
+ if (bssid == null || fileName == null) {
+ Log.e(mTAG, "Invalid arguments for Icon request.");
+ return false;
}
-
- sLocalLog.log(debugLog);
-
- synchronized (sLock) {
- if (startHalNative()) {
- int wlan0Index = queryInterfaceIndex(mInterfaceName);
- if (wlan0Index == -1) {
- if (DBG) sLocalLog.log("Could not find interface with name: " + mInterfaceName);
- return false;
- }
- sWlan0Index = wlan0Index;
- sThread = new MonitorThread();
- sThread.start();
- return true;
- } else {
- if (DBG) sLocalLog.log("Could not start hal");
- Log.e(TAG, "Could not start hal");
- return false;
- }
- }
+ return mSupplicantStaIfaceHal.initiateHs20IconQuery(bssid, fileName);
}
- public void stopHal() {
- synchronized (sLock) {
- if (isHalStarted()) {
- stopHalNative();
- try {
- sThread.join(STOP_HAL_TIMEOUT_MS);
- Log.d(TAG, "HAL event thread stopped successfully");
- } catch (InterruptedException e) {
- Log.e(TAG, "Could not stop HAL cleanly");
- }
- sThread = null;
- sWifiHalHandle = 0;
- sWifiIfaceHandles = null;
- sWlan0Index = -1;
- }
- }
+ /**
+ * Get the currently configured network's WPS NFC token.
+ *
+ * @return Hex string corresponding to the WPS NFC token.
+ */
+ public String getCurrentNetworkWpsNfcConfigurationToken() {
+ return mSupplicantStaIfaceHal.getCurrentNetworkWpsNfcConfigurationToken();
}
+ /** Remove the request |networkId| from supplicant if it's the current network,
+ * if the current configured network matches |networkId|.
+ *
+ * @param networkId network id of the network to be removed from supplicant.
+ */
+ public void removeNetworkIfCurrent(int networkId) {
+ mSupplicantStaIfaceHal.removeNetworkIfCurrent(networkId);
+ }
+
+ /********************************************************
+ * Vendor HAL operations
+ ********************************************************/
+ /**
+ * Callback to notify vendor HAL death.
+ */
+ public interface VendorHalDeathEventHandler {
+ /**
+ * Invoked when the vendor HAL dies.
+ */
+ void onDeath();
+ }
+
+ /**
+ * Initializes the vendor HAL. This is just used to initialize the {@link HalDeviceManager}.
+ */
+ public boolean initializeVendorHal(VendorHalDeathEventHandler handler) {
+ return mWifiVendorHal.initialize(handler);
+ }
+
+ /**
+ * Bring up the Vendor HAL and configure for STA mode or AP mode, if vendor HAL is supported.
+ *
+ * @param isStaMode true to start HAL in STA mode, false to start in AP mode.
+ * @return false if the HAL start fails, true if successful or if vendor HAL not supported.
+ */
+ private boolean startHalIfNecessary(boolean isStaMode) {
+ if (!mWifiVendorHal.isVendorHalSupported()) {
+ Log.i(mTAG, "Vendor HAL not supported, Ignore start...");
+ return true;
+ }
+ return mWifiVendorHal.startVendorHal(isStaMode);
+ }
+
+ /**
+ * Stops the HAL, if vendor HAL is supported.
+ */
+ private void stopHalIfNecessary() {
+ if (!mWifiVendorHal.isVendorHalSupported()) {
+ Log.i(mTAG, "Vendor HAL not supported, Ignore stop...");
+ return;
+ }
+ mWifiVendorHal.stopVendorHal();
+ }
+
+ /**
+ * Tests whether the HAL is running or not
+ */
public boolean isHalStarted() {
- return (sWifiHalHandle != 0);
- }
- private static native int getInterfacesNative();
-
- public int queryInterfaceIndex(String interfaceName) {
- synchronized (sLock) {
- if (isHalStarted()) {
- int num = getInterfacesNative();
- for (int i = 0; i < num; i++) {
- String name = getInterfaceNameNative(i);
- if (name.equals(interfaceName)) {
- return i;
- }
- }
- }
- }
- return -1;
- }
-
- private static native String getInterfaceNameNative(int index);
- public String getInterfaceName(int index) {
- synchronized (sLock) {
- return getInterfaceNameNative(index);
- }
+ return mWifiVendorHal.isHalStarted();
}
// TODO: Change variable names to camel style.
@@ -1644,29 +856,18 @@
public int max_ap_cache_per_scan;
public int max_rssi_sample_size;
public int max_scan_reporting_threshold;
- public int max_hotlist_bssids;
- public int max_significant_wifi_change_aps;
- public int max_bssid_history_entries;
- public int max_number_epno_networks;
- public int max_number_epno_networks_by_ssid;
- public int max_number_of_white_listed_ssid;
}
- public boolean getScanCapabilities(ScanCapabilities capabilities) {
- synchronized (sLock) {
- return isHalStarted() && getScanCapabilitiesNative(sWlan0Index, capabilities);
- }
+ /**
+ * Gets the scan capabilities
+ *
+ * @param capabilities object to be filled in
+ * @return true for success. false for failure
+ */
+ public boolean getBgScanCapabilities(ScanCapabilities capabilities) {
+ return mWifiVendorHal.getBgScanCapabilities(capabilities);
}
- private static native boolean getScanCapabilitiesNative(
- int iface, ScanCapabilities capabilities);
-
- private static native boolean startScanNative(int iface, int id, ScanSettings settings);
- private static native boolean stopScanNative(int iface, int id);
- private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
- private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
- private static native void setWifiLinkLayerStatsNative(int iface, int enable);
-
public static class ChannelSettings {
public int frequency;
public int dwell_time_ms;
@@ -1684,14 +885,37 @@
public ChannelSettings[] channels;
}
+ /**
+ * Network parameters for hidden networks to be scanned for.
+ */
+ public static class HiddenNetwork {
+ public String ssid;
+
+ @Override
+ public boolean equals(Object otherObj) {
+ if (this == otherObj) {
+ return true;
+ } else if (otherObj == null || getClass() != otherObj.getClass()) {
+ return false;
+ }
+ HiddenNetwork other = (HiddenNetwork) otherObj;
+ return Objects.equals(ssid, other.ssid);
+ }
+
+ @Override
+ public int hashCode() {
+ return (ssid == null ? 0 : ssid.hashCode());
+ }
+ }
+
public static class ScanSettings {
public int base_period_ms;
public int max_ap_per_scan;
public int report_threshold_percent;
public int report_threshold_num_scans;
public int num_buckets;
- /* Not part of gscan HAL API. Used only for wpa_supplicant scanning */
- public int[] hiddenNetworkIds;
+ /* Not used for bg scans. Only works for single scans. */
+ public HiddenNetwork[] hiddenNetworks;
public BucketSettings[] buckets;
}
@@ -1700,8 +924,6 @@
*/
public static class PnoNetwork {
public String ssid;
- public int networkId;
- public int priority;
public byte flags;
public byte auth_bit_field;
@@ -1713,10 +935,16 @@
return false;
}
PnoNetwork other = (PnoNetwork) otherObj;
- return ((Objects.equals(ssid, other.ssid)) && (networkId == other.networkId)
- && (priority == other.priority) && (flags == other.flags)
+ return ((Objects.equals(ssid, other.ssid)) && (flags == other.flags)
&& (auth_bit_field == other.auth_bit_field));
}
+
+ @Override
+ public int hashCode() {
+ int result = (ssid == null ? 0 : ssid.hashCode());
+ result ^= ((int) flags * 31) + ((int) auth_bit_field << 8);
+ return result;
+ }
}
/**
@@ -1731,21 +959,11 @@
public int sameNetworkBonus;
public int secureBonus;
public int band5GHzBonus;
+ public int periodInMs;
public boolean isConnected;
public PnoNetwork[] networkList;
}
- /**
- * Wi-Fi channel information.
- */
- public static class WifiChannelInfo {
- int mPrimaryFrequency;
- int mCenterFrequency0;
- int mCenterFrequency1;
- int mChannelWidth;
- // TODO: add preamble once available in HAL.
- }
-
public static interface ScanEventHandler {
/**
* Called for each AP as it is found with the entire contents of the beacon/probe response.
@@ -1783,775 +1001,245 @@
void onPnoScanFailed();
}
- /* scan status, keep these values in sync with gscan.h */
public static final int WIFI_SCAN_RESULTS_AVAILABLE = 0;
public static final int WIFI_SCAN_THRESHOLD_NUM_SCANS = 1;
public static final int WIFI_SCAN_THRESHOLD_PERCENT = 2;
public static final int WIFI_SCAN_FAILED = 3;
- // Callback from native
- private static void onScanStatus(int id, int event) {
- ScanEventHandler handler = sScanEventHandler;
- if (handler != null) {
- handler.onScanStatus(event);
- }
+ /**
+ * Starts a background scan.
+ * Any ongoing scan will be stopped first
+ *
+ * @param settings to control the scan
+ * @param eventHandler to call with the results
+ * @return true for success
+ */
+ public boolean startBgScan(ScanSettings settings, ScanEventHandler eventHandler) {
+ return mWifiVendorHal.startBgScan(settings, eventHandler);
}
- public static WifiSsid createWifiSsid(byte[] rawSsid) {
- String ssidHexString = String.valueOf(HexEncoding.encode(rawSsid));
-
- if (ssidHexString == null) {
- return null;
- }
-
- WifiSsid wifiSsid = WifiSsid.createFromHex(ssidHexString);
-
- return wifiSsid;
+ /**
+ * Stops any ongoing backgound scan
+ */
+ public void stopBgScan() {
+ mWifiVendorHal.stopBgScan();
}
- public static String ssidConvert(byte[] rawSsid) {
- String ssid;
-
- CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
- try {
- CharBuffer decoded = decoder.decode(ByteBuffer.wrap(rawSsid));
- ssid = decoded.toString();
- } catch (CharacterCodingException cce) {
- ssid = null;
- }
-
- if (ssid == null) {
- ssid = new String(rawSsid, StandardCharsets.ISO_8859_1);
- }
-
- return ssid;
+ /**
+ * Pauses an ongoing backgound scan
+ */
+ public void pauseBgScan() {
+ mWifiVendorHal.pauseBgScan();
}
- // Called from native
- public static boolean setSsid(byte[] rawSsid, ScanResult result) {
- if (rawSsid == null || rawSsid.length == 0 || result == null) {
- return false;
- }
-
- result.SSID = ssidConvert(rawSsid);
- result.wifiSsid = createWifiSsid(rawSsid);
- return true;
+ /**
+ * Restarts a paused scan
+ */
+ public void restartBgScan() {
+ mWifiVendorHal.restartBgScan();
}
- private static void populateScanResult(ScanResult result, int beaconCap, String dbg) {
- if (dbg == null) dbg = "";
-
- InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation();
- InformationElementUtil.VhtOperation vhtOperation =
- new InformationElementUtil.VhtOperation();
- InformationElementUtil.ExtendedCapabilities extendedCaps =
- new InformationElementUtil.ExtendedCapabilities();
-
- ScanResult.InformationElement elements[] =
- InformationElementUtil.parseInformationElements(result.bytes);
- for (ScanResult.InformationElement ie : elements) {
- if(ie.id == ScanResult.InformationElement.EID_HT_OPERATION) {
- htOperation.from(ie);
- } else if(ie.id == ScanResult.InformationElement.EID_VHT_OPERATION) {
- vhtOperation.from(ie);
- } else if (ie.id == ScanResult.InformationElement.EID_EXTENDED_CAPS) {
- extendedCaps.from(ie);
- }
- }
-
- if (extendedCaps.is80211McRTTResponder) {
- result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
- } else {
- result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
- }
-
- //handle RTT related information
- if (vhtOperation.isValid()) {
- result.channelWidth = vhtOperation.getChannelWidth();
- result.centerFreq0 = vhtOperation.getCenterFreq0();
- result.centerFreq1 = vhtOperation.getCenterFreq1();
- } else {
- result.channelWidth = htOperation.getChannelWidth();
- result.centerFreq0 = htOperation.getCenterFreq0(result.frequency);
- result.centerFreq1 = 0;
- }
-
- // build capabilities string
- BitSet beaconCapBits = new BitSet(16);
- for (int i = 0; i < 16; i++) {
- if ((beaconCap & (1 << i)) != 0) {
- beaconCapBits.set(i);
- }
- }
- result.capabilities = InformationElementUtil.Capabilities.buildCapabilities(elements,
- beaconCapBits);
-
- if(DBG) {
- Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth
- + " PrimaryFreq: " + result.frequency + " mCenterfreq0: " + result.centerFreq0
- + " mCenterfreq1: " + result.centerFreq1 + (extendedCaps.is80211McRTTResponder
- ? "Support RTT reponder: " : "Do not support RTT responder")
- + " Capabilities: " + result.capabilities);
- }
-
- result.informationElements = elements;
- }
-
- // Callback from native
- private static void onFullScanResult(int id, ScanResult result,
- int bucketsScanned, int beaconCap) {
- if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID);
-
- ScanEventHandler handler = sScanEventHandler;
- if (handler != null) {
- populateScanResult(result, beaconCap, " onFullScanResult ");
- handler.onFullScanResult(result, bucketsScanned);
- }
- }
-
- private static int sScanCmdId = 0;
- private static ScanEventHandler sScanEventHandler;
- private static ScanSettings sScanSettings;
-
- public boolean startScan(ScanSettings settings, ScanEventHandler eventHandler) {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sScanCmdId != 0) {
- stopScan();
- } else if (sScanSettings != null || sScanEventHandler != null) {
- /* current scan is paused; no need to stop it */
- }
-
- sScanCmdId = getNewCmdIdLocked();
-
- sScanSettings = settings;
- sScanEventHandler = eventHandler;
-
- if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
- sScanEventHandler = null;
- sScanSettings = null;
- sScanCmdId = 0;
- return false;
- }
-
- return true;
- } else {
- return false;
- }
- }
- }
-
- public void stopScan() {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sScanCmdId != 0) {
- stopScanNative(sWlan0Index, sScanCmdId);
- }
- sScanSettings = null;
- sScanEventHandler = null;
- sScanCmdId = 0;
- }
- }
- }
-
- public void pauseScan() {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
- Log.d(TAG, "Pausing scan");
- WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
- stopScanNative(sWlan0Index, sScanCmdId);
- sScanCmdId = 0;
- sScanEventHandler.onScanPaused(scanData);
- }
- }
- }
- }
-
- public void restartScan() {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
- Log.d(TAG, "Restarting scan");
- ScanEventHandler handler = sScanEventHandler;
- ScanSettings settings = sScanSettings;
- if (startScan(sScanSettings, sScanEventHandler)) {
- sScanEventHandler.onScanRestarted();
- } else {
- /* we are still paused; don't change state */
- sScanEventHandler = handler;
- sScanSettings = settings;
- }
- }
- }
- }
- }
-
- public WifiScanner.ScanData[] getScanResults(boolean flush) {
- synchronized (sLock) {
- WifiScanner.ScanData[] sd = null;
- if (isHalStarted()) {
- sd = getScanResultsNative(sWlan0Index, flush);
- }
-
- if (sd != null) {
- return sd;
- } else {
- return new WifiScanner.ScanData[0];
- }
- }
- }
-
- public static interface HotlistEventHandler {
- void onHotlistApFound (ScanResult[] result);
- void onHotlistApLost (ScanResult[] result);
- }
-
- private static int sHotlistCmdId = 0;
- private static HotlistEventHandler sHotlistEventHandler;
-
- private native static boolean setHotlistNative(int iface, int id,
- WifiScanner.HotlistSettings settings);
- private native static boolean resetHotlistNative(int iface, int id);
-
- public boolean setHotlist(WifiScanner.HotlistSettings settings,
- HotlistEventHandler eventHandler) {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sHotlistCmdId != 0) {
- return false;
- } else {
- sHotlistCmdId = getNewCmdIdLocked();
- }
-
- sHotlistEventHandler = eventHandler;
- if (setHotlistNative(sWlan0Index, sHotlistCmdId, settings) == false) {
- sHotlistEventHandler = null;
- return false;
- }
-
- return true;
- } else {
- return false;
- }
- }
- }
-
- public void resetHotlist() {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sHotlistCmdId != 0) {
- resetHotlistNative(sWlan0Index, sHotlistCmdId);
- sHotlistCmdId = 0;
- sHotlistEventHandler = null;
- }
- }
- }
- }
-
- // Callback from native
- private static void onHotlistApFound(int id, ScanResult[] results) {
- HotlistEventHandler handler = sHotlistEventHandler;
- if (handler != null) {
- handler.onHotlistApFound(results);
- } else {
- /* this can happen because of race conditions */
- Log.d(TAG, "Ignoring hotlist AP found event");
- }
- }
-
- // Callback from native
- private static void onHotlistApLost(int id, ScanResult[] results) {
- HotlistEventHandler handler = sHotlistEventHandler;
- if (handler != null) {
- handler.onHotlistApLost(results);
- } else {
- /* this can happen because of race conditions */
- Log.d(TAG, "Ignoring hotlist AP lost event");
- }
- }
-
- public static interface SignificantWifiChangeEventHandler {
- void onChangesFound(ScanResult[] result);
- }
-
- private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
- private static int sSignificantWifiChangeCmdId;
-
- private static native boolean trackSignificantWifiChangeNative(
- int iface, int id, WifiScanner.WifiChangeSettings settings);
- private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
-
- public boolean trackSignificantWifiChange(
- WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sSignificantWifiChangeCmdId != 0) {
- return false;
- } else {
- sSignificantWifiChangeCmdId = getNewCmdIdLocked();
- }
-
- sSignificantWifiChangeHandler = handler;
- if (trackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId,
- settings) == false) {
- sSignificantWifiChangeHandler = null;
- return false;
- }
-
- return true;
- } else {
- return false;
- }
-
- }
- }
-
- public void untrackSignificantWifiChange() {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sSignificantWifiChangeCmdId != 0) {
- untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
- sSignificantWifiChangeCmdId = 0;
- sSignificantWifiChangeHandler = null;
- }
- }
- }
- }
-
- // Callback from native
- private static void onSignificantWifiChange(int id, ScanResult[] results) {
- SignificantWifiChangeEventHandler handler = sSignificantWifiChangeHandler;
- if (handler != null) {
- handler.onChangesFound(results);
- } else {
- /* this can happen because of race conditions */
- Log.d(TAG, "Ignoring significant wifi change");
- }
+ /**
+ * Gets the latest scan results received.
+ */
+ public WifiScanner.ScanData[] getBgScanResults() {
+ return mWifiVendorHal.getBgScanResults();
}
public WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
- // TODO: use correct iface name to Index translation
- if (iface == null) return null;
- synchronized (sLock) {
- if (isHalStarted()) {
- return getWifiLinkLayerStatsNative(sWlan0Index);
- } else {
- return null;
- }
- }
+ return mWifiVendorHal.getWifiLinkLayerStats();
}
- public void setWifiLinkLayerStats(String iface, int enable) {
- if (iface == null) return;
- synchronized (sLock) {
- if (isHalStarted()) {
- setWifiLinkLayerStatsNative(sWlan0Index, enable);
- }
- }
- }
-
- public static native int getSupportedFeatureSetNative(int iface);
+ /**
+ * Get the supported features
+ *
+ * @return bitmask defined by WifiManager.WIFI_FEATURE_*
+ */
public int getSupportedFeatureSet() {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getSupportedFeatureSetNative(sWlan0Index);
- } else {
- Log.d(TAG, "Failing getSupportedFeatureset because HAL isn't started");
- return 0;
- }
- }
+ return mWifiVendorHal.getSupportedFeatureSet();
}
- /* Rtt related commands/events */
public static interface RttEventHandler {
void onRttResults(RttManager.RttResult[] result);
}
- private static RttEventHandler sRttEventHandler;
- private static int sRttCmdId;
-
- // Callback from native
- private static void onRttResults(int id, RttManager.RttResult[] results) {
- RttEventHandler handler = sRttEventHandler;
- if (handler != null && id == sRttCmdId) {
- Log.d(TAG, "Received " + results.length + " rtt results");
- handler.onRttResults(results);
- sRttCmdId = 0;
- } else {
- Log.d(TAG, "RTT Received event for unknown cmd = " + id +
- ", current id = " + sRttCmdId);
- }
- }
-
- private static native boolean requestRangeNative(
- int iface, int id, RttManager.RttParams[] params);
- private static native boolean cancelRangeRequestNative(
- int iface, int id, RttManager.RttParams[] params);
-
+ /**
+ * Starts a new rtt request
+ *
+ * @param params RTT request params. Refer to {@link RttManager#RttParams}.
+ * @param handler Callback to be invoked to notify any results.
+ * @return true if the request was successful, false otherwise.
+ */
public boolean requestRtt(
RttManager.RttParams[] params, RttEventHandler handler) {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sRttCmdId != 0) {
- Log.w(TAG, "Last one is still under measurement!");
- return false;
- } else {
- sRttCmdId = getNewCmdIdLocked();
- }
- sRttEventHandler = handler;
- return requestRangeNative(sWlan0Index, sRttCmdId, params);
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.requestRtt(params, handler);
}
+ /**
+ * Cancels an outstanding rtt request
+ *
+ * @param params RTT request params. Refer to {@link RttManager#RttParams}
+ * @return true if there was an outstanding request and it was successfully cancelled
+ */
public boolean cancelRtt(RttManager.RttParams[] params) {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sRttCmdId == 0) {
- return false;
- }
-
- sRttCmdId = 0;
-
- if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
- sRttEventHandler = null;
- return true;
- } else {
- Log.e(TAG, "RTT cancel Request failed");
- return false;
- }
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.cancelRtt(params);
}
- private static int sRttResponderCmdId = 0;
-
- private static native ResponderConfig enableRttResponderNative(int iface, int commandId,
- int timeoutSeconds, WifiChannelInfo channelHint);
/**
* Enable RTT responder role on the device. Returns {@link ResponderConfig} if the responder
* role is successfully enabled, {@code null} otherwise.
+ *
+ * @param timeoutSeconds timeout to use for the responder.
*/
@Nullable
public ResponderConfig enableRttResponder(int timeoutSeconds) {
- synchronized (sLock) {
- if (!isHalStarted()) return null;
- if (sRttResponderCmdId != 0) {
- if (DBG) Log.e(mTAG, "responder mode already enabled - this shouldn't happen");
- return null;
- }
- int id = getNewCmdIdLocked();
- ResponderConfig config = enableRttResponderNative(
- sWlan0Index, id, timeoutSeconds, null);
- if (config != null) sRttResponderCmdId = id;
- if (DBG) Log.d(TAG, "enabling rtt " + (config != null));
- return config;
- }
+ return mWifiVendorHal.enableRttResponder(timeoutSeconds);
}
- private static native boolean disableRttResponderNative(int iface, int commandId);
/**
* Disable RTT responder role. Returns {@code true} if responder role is successfully disabled,
* {@code false} otherwise.
*/
public boolean disableRttResponder() {
- synchronized (sLock) {
- if (!isHalStarted()) return false;
- if (sRttResponderCmdId == 0) {
- Log.e(mTAG, "responder role not enabled yet");
- return true;
- }
- sRttResponderCmdId = 0;
- return disableRttResponderNative(sWlan0Index, sRttResponderCmdId);
- }
+ return mWifiVendorHal.disableRttResponder();
}
- private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
-
+ /**
+ * Set the MAC OUI during scanning.
+ * An OUI {Organizationally Unique Identifier} is a 24-bit number that
+ * uniquely identifies a vendor or manufacturer.
+ *
+ * @param oui OUI to set.
+ * @return true for success
+ */
public boolean setScanningMacOui(byte[] oui) {
- synchronized (sLock) {
- if (isHalStarted()) {
- return setScanningMacOuiNative(sWlan0Index, oui);
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.setScanningMacOui(oui);
}
- private static native int[] getChannelsForBandNative(
- int iface, int band);
-
+ /**
+ * Query the list of valid frequencies for the provided band.
+ * The result depends on the on the country code that has been set.
+ *
+ * @param band as specified by one of the WifiScanner.WIFI_BAND_* constants.
+ * @return frequencies vector of valid frequencies (MHz), or null for error.
+ * @throws IllegalArgumentException if band is not recognized.
+ */
public int [] getChannelsForBand(int band) {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getChannelsForBandNative(sWlan0Index, band);
- } else {
- return null;
- }
- }
+ return mWifiVendorHal.getChannelsForBand(band);
}
- private static native boolean isGetChannelsForBandSupportedNative();
- public boolean isGetChannelsForBandSupported(){
- synchronized (sLock) {
- if (isHalStarted()) {
- return isGetChannelsForBandSupportedNative();
- } else {
- return false;
- }
- }
+ /**
+ * Indicates whether getChannelsForBand is supported.
+ *
+ * @return true if it is.
+ */
+ public boolean isGetChannelsForBandSupported() {
+ return mWifiVendorHal.isGetChannelsForBandSupported();
}
- private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
- public boolean setDfsFlag(boolean dfsOn) {
- synchronized (sLock) {
- if (isHalStarted()) {
- return setDfsFlagNative(sWlan0Index, dfsOn);
- } else {
- return false;
- }
- }
- }
-
- private static native boolean setInterfaceUpNative(boolean up);
- public boolean setInterfaceUp(boolean up) {
- synchronized (sLock) {
- if (isHalStarted()) {
- return setInterfaceUpNative(up);
- } else {
- return false;
- }
- }
- }
-
- private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
+ /**
+ * RTT (Round Trip Time) measurement capabilities of the device.
+ */
public RttManager.RttCapabilities getRttCapabilities() {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getRttCapabilitiesNative(sWlan0Index);
- } else {
- return null;
- }
- }
+ return mWifiVendorHal.getRttCapabilities();
}
- private static native ApfCapabilities getApfCapabilitiesNative(int iface);
+ /**
+ * Get the APF (Android Packet Filter) capabilities of the device
+ */
public ApfCapabilities getApfCapabilities() {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getApfCapabilitiesNative(sWlan0Index);
- } else {
- return null;
- }
- }
+ return mWifiVendorHal.getApfCapabilities();
}
- private static native boolean installPacketFilterNative(int iface, byte[] filter);
+ /**
+ * Installs an APF program on this iface, replacing any existing program.
+ *
+ * @param filter is the android packet filter program
+ * @return true for success
+ */
public boolean installPacketFilter(byte[] filter) {
- synchronized (sLock) {
- if (isHalStarted()) {
- return installPacketFilterNative(sWlan0Index, filter);
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.installPacketFilter(filter);
}
- private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
- public boolean setCountryCodeHal(String CountryCode) {
- synchronized (sLock) {
- if (isHalStarted()) {
- return setCountryCodeHalNative(sWlan0Index, CountryCode);
- } else {
- return false;
- }
- }
- }
-
- /* Rtt related commands/events */
- public abstract class TdlsEventHandler {
- abstract public void onTdlsStatus(String macAddr, int status, int reason);
- }
-
- private static TdlsEventHandler sTdlsEventHandler;
-
- private static native boolean enableDisableTdlsNative(int iface, boolean enable,
- String macAddr);
- public boolean enableDisableTdls(boolean enable, String macAdd, TdlsEventHandler tdlsCallBack) {
- synchronized (sLock) {
- sTdlsEventHandler = tdlsCallBack;
- return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
- }
- }
-
- // Once TDLS per mac and event feature is implemented, this class definition should be
- // moved to the right place, like WifiManager etc
- public static class TdlsStatus {
- int channel;
- int global_operating_class;
- int state;
- int reason;
- }
- private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
- public TdlsStatus getTdlsStatus(String macAdd) {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getTdlsStatusNative(sWlan0Index, macAdd);
- } else {
- return null;
- }
- }
- }
-
- //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
- // moved to the right place, like WifiStateMachine etc
- public static class TdlsCapabilities {
- /* Maximum TDLS session number can be supported by the Firmware and hardware */
- int maxConcurrentTdlsSessionNumber;
- boolean isGlobalTdlsSupported;
- boolean isPerMacTdlsSupported;
- boolean isOffChannelTdlsSupported;
- }
-
-
-
- private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
- public TdlsCapabilities getTdlsCapabilities () {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getTdlsCapabilitiesNative(sWlan0Index);
- } else {
- return null;
- }
- }
- }
-
- private static boolean onTdlsStatus(String macAddr, int status, int reason) {
- TdlsEventHandler handler = sTdlsEventHandler;
- if (handler == null) {
- return false;
- } else {
- handler.onTdlsStatus(macAddr, status, reason);
- return true;
- }
+ /**
+ * Set country code for this AP iface.
+ *
+ * @param countryCode - two-letter country code (as ISO 3166)
+ * @return true for success
+ */
+ public boolean setCountryCodeHal(String countryCode) {
+ return mWifiVendorHal.setCountryCodeHal(countryCode);
}
//---------------------------------------------------------------------------------
-
/* Wifi Logger commands/events */
-
public static interface WifiLoggerEventHandler {
void onRingBufferData(RingBufferStatus status, byte[] buffer);
void onWifiAlert(int errorCode, byte[] buffer);
}
- private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
-
- // Callback from native
- private static void onRingBufferData(RingBufferStatus status, byte[] buffer) {
- WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
- if (handler != null)
- handler.onRingBufferData(status, buffer);
- }
-
- // Callback from native
- private static void onWifiAlert(byte[] buffer, int errorCode) {
- WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
- if (handler != null)
- handler.onWifiAlert(errorCode, buffer);
- }
-
- private static int sLogCmdId = -1;
- private static native boolean setLoggingEventHandlerNative(int iface, int id);
+ /**
+ * Registers the logger callback and enables alerts.
+ * Ring buffer data collection is only triggered when |startLoggingRingBuffer| is invoked.
+ *
+ * @param handler Callback to be invoked.
+ * @return true on success, false otherwise.
+ */
public boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
- synchronized (sLock) {
- if (isHalStarted()) {
- int oldId = sLogCmdId;
- sLogCmdId = getNewCmdIdLocked();
- if (!setLoggingEventHandlerNative(sWlan0Index, sLogCmdId)) {
- sLogCmdId = oldId;
- return false;
- }
- sWifiLoggerEventHandler = handler;
- return true;
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.setLoggingEventHandler(handler);
}
- private static native boolean startLoggingRingBufferNative(int iface, int verboseLevel,
- int flags, int minIntervalSec ,int minDataSize, String ringName);
+ /**
+ * Control debug data collection
+ *
+ * @param verboseLevel 0 to 3, inclusive. 0 stops logging.
+ * @param flags Ignored.
+ * @param maxInterval Maximum interval between reports; ignore if 0.
+ * @param minDataSize Minimum data size in buffer for report; ignore if 0.
+ * @param ringName Name of the ring for which data collection is to start.
+ * @return true for success, false otherwise.
+ */
public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
int minDataSize, String ringName){
- synchronized (sLock) {
- if (isHalStarted()) {
- return startLoggingRingBufferNative(sWlan0Index, verboseLevel, flags, maxInterval,
- minDataSize, ringName);
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.startLoggingRingBuffer(
+ verboseLevel, flags, maxInterval, minDataSize, ringName);
}
- private static native int getSupportedLoggerFeatureSetNative(int iface);
+ /**
+ * Logger features exposed.
+ * This is a no-op now, will always return -1.
+ *
+ * @return true on success, false otherwise.
+ */
public int getSupportedLoggerFeatureSet() {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getSupportedLoggerFeatureSetNative(sWlan0Index);
- } else {
- return 0;
- }
- }
+ return mWifiVendorHal.getSupportedLoggerFeatureSet();
}
- private static native boolean resetLogHandlerNative(int iface, int id);
+ /**
+ * Stops all logging and resets the logger callback.
+ * This stops both the alerts and ring buffer data collection.
+ * @return true on success, false otherwise.
+ */
public boolean resetLogHandler() {
- synchronized (sLock) {
- if (isHalStarted()) {
- if (sLogCmdId == -1) {
- Log.e(TAG,"Can not reset handler Before set any handler");
- return false;
- }
- sWifiLoggerEventHandler = null;
- if (resetLogHandlerNative(sWlan0Index, sLogCmdId)) {
- sLogCmdId = -1;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.resetLogHandler();
}
- private static native String getDriverVersionNative(int iface);
+ /**
+ * Vendor-provided wifi driver version string
+ *
+ * @return String returned from the HAL.
+ */
public String getDriverVersion() {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getDriverVersionNative(sWlan0Index);
- } else {
- return "";
- }
- }
+ return mWifiVendorHal.getDriverVersion();
}
-
- private static native String getFirmwareVersionNative(int iface);
+ /**
+ * Vendor-provided wifi firmware version string
+ *
+ * @return String returned from the HAL.
+ */
public String getFirmwareVersion() {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getFirmwareVersionNative(sWlan0Index);
- } else {
- return "";
- }
- }
+ return mWifiVendorHal.getFirmwareVersion();
}
public static class RingBufferStatus{
@@ -2564,6 +1252,11 @@
int readBytes;
int writtenRecords;
+ // Bit masks for interpreting |flag|
+ public static final int HAS_BINARY_ENTRIES = (1 << 0);
+ public static final int HAS_ASCII_ENTRIES = (1 << 1);
+ public static final int HAS_PER_PACKET_ENTRIES = (1 << 2);
+
@Override
public String toString() {
return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
@@ -2573,64 +1266,39 @@
}
}
- private static native RingBufferStatus[] getRingBufferStatusNative(int iface);
+ /**
+ * API to get the status of all ring buffers supported by driver
+ */
public RingBufferStatus[] getRingBufferStatus() {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getRingBufferStatusNative(sWlan0Index);
- } else {
- return null;
- }
- }
+ return mWifiVendorHal.getRingBufferStatus();
}
- private static native boolean getRingBufferDataNative(int iface, String ringName);
+ /**
+ * Indicates to driver that all the data has to be uploaded urgently
+ *
+ * @param ringName Name of the ring buffer requested.
+ * @return true on success, false otherwise.
+ */
public boolean getRingBufferData(String ringName) {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getRingBufferDataNative(sWlan0Index, ringName);
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.getRingBufferData(ringName);
}
- private static byte[] mFwMemoryDump;
- // Callback from native
- private static void onWifiFwMemoryAvailable(byte[] buffer) {
- mFwMemoryDump = buffer;
- if (DBG) {
- Log.d(TAG, "onWifiFwMemoryAvailable is called and buffer length is: " +
- (buffer == null ? 0 : buffer.length));
- }
- }
-
- private static native boolean getFwMemoryDumpNative(int iface);
+ /**
+ * Request vendor debug info from the firmware
+ *
+ * @return Raw data obtained from the HAL.
+ */
public byte[] getFwMemoryDump() {
- synchronized (sLock) {
- if (isHalStarted()) {
- if(getFwMemoryDumpNative(sWlan0Index)) {
- byte[] fwMemoryDump = mFwMemoryDump;
- mFwMemoryDump = null;
- return fwMemoryDump;
- } else {
- return null;
- }
- }
- return null;
- }
+ return mWifiVendorHal.getFwMemoryDump();
}
- private static native byte[] getDriverStateDumpNative(int iface);
- /** Fetch the driver state, for driver debugging. */
+ /**
+ * Request vendor debug info from the driver
+ *
+ * @return Raw data obtained from the HAL.
+ */
public byte[] getDriverStateDump() {
- synchronized (sLock) {
- if (isHalStarted()) {
- return getDriverStateDumpNative(sWlan0Index);
- } else {
- return null;
- }
- }
+ return mWifiVendorHal.getDriverStateDump();
}
//---------------------------------------------------------------------------------
@@ -2834,67 +1502,31 @@
}
}
- private static native int startPktFateMonitoringNative(int iface);
/**
* Ask the HAL to enable packet fate monitoring. Fails unless HAL is started.
+ *
+ * @return true for success, false otherwise.
*/
public boolean startPktFateMonitoring() {
- synchronized (sLock) {
- if (isHalStarted()) {
- return startPktFateMonitoringNative(sWlan0Index) == WIFI_SUCCESS;
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.startPktFateMonitoring();
}
- private static native int getTxPktFatesNative(int iface, TxFateReport[] reportBufs);
/**
* Fetch the most recent TX packet fates from the HAL. Fails unless HAL is started.
+ *
+ * @return true for success, false otherwise.
*/
public boolean getTxPktFates(TxFateReport[] reportBufs) {
- synchronized (sLock) {
- if (isHalStarted()) {
- int res = getTxPktFatesNative(sWlan0Index, reportBufs);
- if (res != WIFI_SUCCESS) {
- Log.e(TAG, "getTxPktFatesNative returned " + res);
- return false;
- } else {
- return true;
- }
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.getTxPktFates(reportBufs);
}
- private static native int getRxPktFatesNative(int iface, RxFateReport[] reportBufs);
/**
* Fetch the most recent RX packet fates from the HAL. Fails unless HAL is started.
*/
public boolean getRxPktFates(RxFateReport[] reportBufs) {
- synchronized (sLock) {
- if (isHalStarted()) {
- int res = getRxPktFatesNative(sWlan0Index, reportBufs);
- if (res != WIFI_SUCCESS) {
- Log.e(TAG, "getRxPktFatesNative returned " + res);
- return false;
- } else {
- return true;
- }
- } else {
- return false;
- }
- }
+ return mWifiVendorHal.getRxPktFates(reportBufs);
}
- //---------------------------------------------------------------------------------
- /* Configure ePNO/PNO */
- private static PnoEventHandler sPnoEventHandler;
- private static int sPnoCmdId = 0;
-
- private static native boolean setPnoListNative(int iface, int id, PnoSettings settings);
-
/**
* Set the PNO settings & the network list in HAL to start PNO.
* @param settings PNO settings and network list.
@@ -2902,229 +1534,176 @@
* @return true if success, false otherwise
*/
public boolean setPnoList(PnoSettings settings, PnoEventHandler eventHandler) {
- Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
-
- synchronized (sLock) {
- if (isHalStarted()) {
- sPnoCmdId = getNewCmdIdLocked();
- sPnoEventHandler = eventHandler;
- if (setPnoListNative(sWlan0Index, sPnoCmdId, settings)) {
- return true;
- }
- }
- sPnoEventHandler = null;
- return false;
- }
+ Log.e(mTAG, "setPnoList not supported");
+ return false;
}
/**
- * Set the PNO network list in HAL to start PNO.
- * @param list PNO network list.
- * @param eventHandler Handler to receive notifications back during PNO scan.
- * @return true if success, false otherwise
- */
- public boolean setPnoList(PnoNetwork[] list, PnoEventHandler eventHandler) {
- PnoSettings settings = new PnoSettings();
- settings.networkList = list;
- return setPnoList(settings, eventHandler);
- }
-
- private static native boolean resetPnoListNative(int iface, int id);
-
- /**
* Reset the PNO settings in HAL to stop PNO.
* @return true if success, false otherwise
*/
public boolean resetPnoList() {
- Log.e(TAG, "resetPnoList cmd " + sPnoCmdId);
-
- synchronized (sLock) {
- if (isHalStarted()) {
- sPnoCmdId = getNewCmdIdLocked();
- sPnoEventHandler = null;
- if (resetPnoListNative(sWlan0Index, sPnoCmdId)) {
- return true;
- }
- }
- return false;
- }
+ Log.e(mTAG, "resetPnoList not supported");
+ return false;
}
- // Callback from native
- private static void onPnoNetworkFound(int id, ScanResult[] results, int[] beaconCaps) {
- if (results == null) {
- Log.e(TAG, "onPnoNetworkFound null results");
- return;
-
- }
- Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
-
- PnoEventHandler handler = sPnoEventHandler;
- if (sPnoCmdId != 0 && handler != null) {
- for (int i=0; i<results.length; i++) {
- Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
- + " " + results[i].level + " " + results[i].frequency);
-
- populateScanResult(results[i], beaconCaps[i], "onPnoNetworkFound ");
- results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
- }
-
- handler.onPnoNetworkFound(results);
- } else {
- /* this can happen because of race conditions */
- Log.d(TAG, "Ignoring Pno Network found event");
- }
- }
-
- private native static boolean setBssidBlacklistNative(int iface, int id,
- String list[]);
-
- public boolean setBssidBlacklist(String list[]) {
- int size = 0;
- if (list != null) {
- size = list.length;
- }
- Log.e(TAG, "setBssidBlacklist cmd " + sPnoCmdId + " size " + size);
-
- synchronized (sLock) {
- if (isHalStarted()) {
- sPnoCmdId = getNewCmdIdLocked();
- return setBssidBlacklistNative(sWlan0Index, sPnoCmdId, list);
- } else {
- return false;
- }
- }
- }
-
- private native static int startSendingOffloadedPacketNative(int iface, int idx,
- byte[] srcMac, byte[] dstMac, byte[] pktData, int period);
-
- public int
- startSendingOffloadedPacket(int slot, KeepalivePacketData keepAlivePacket, int period) {
- Log.d(TAG, "startSendingOffloadedPacket slot=" + slot + " period=" + period);
-
+ /**
+ * Start sending the specified keep alive packets periodically.
+ *
+ * @param slot Integer used to identify each request.
+ * @param keepAlivePacket Raw packet contents to send.
+ * @param period Period to use for sending these packets.
+ * @return 0 for success, -1 for error
+ */
+ public int startSendingOffloadedPacket(int slot, KeepalivePacketData keepAlivePacket,
+ int period) {
String[] macAddrStr = getMacAddress().split(":");
byte[] srcMac = new byte[6];
- for(int i = 0; i < 6; i++) {
+ for (int i = 0; i < 6; i++) {
Integer hexVal = Integer.parseInt(macAddrStr[i], 16);
srcMac[i] = hexVal.byteValue();
}
- synchronized (sLock) {
- if (isHalStarted()) {
- return startSendingOffloadedPacketNative(sWlan0Index, slot, srcMac,
- keepAlivePacket.dstMac, keepAlivePacket.data, period);
- } else {
- return -1;
- }
- }
+ return mWifiVendorHal.startSendingOffloadedPacket(
+ slot, srcMac, keepAlivePacket, period);
}
- private native static int stopSendingOffloadedPacketNative(int iface, int idx);
-
- public int
- stopSendingOffloadedPacket(int slot) {
- Log.d(TAG, "stopSendingOffloadedPacket " + slot);
- synchronized (sLock) {
- if (isHalStarted()) {
- return stopSendingOffloadedPacketNative(sWlan0Index, slot);
- } else {
- return -1;
- }
- }
+ /**
+ * Stop sending the specified keep alive packets.
+ *
+ * @param slot id - same as startSendingOffloadedPacket call.
+ * @return 0 for success, -1 for error
+ */
+ public int stopSendingOffloadedPacket(int slot) {
+ return mWifiVendorHal.stopSendingOffloadedPacket(slot);
}
public static interface WifiRssiEventHandler {
void onRssiThresholdBreached(byte curRssi);
}
- private static WifiRssiEventHandler sWifiRssiEventHandler;
-
- // Callback from native
- private static void onRssiThresholdBreached(int id, byte curRssi) {
- WifiRssiEventHandler handler = sWifiRssiEventHandler;
- if (handler != null) {
- handler.onRssiThresholdBreached(curRssi);
- }
- }
-
- private native static int startRssiMonitoringNative(int iface, int id,
- byte maxRssi, byte minRssi);
-
- private static int sRssiMonitorCmdId = 0;
-
+ /**
+ * Start RSSI monitoring on the currently connected access point.
+ *
+ * @param maxRssi Maximum RSSI threshold.
+ * @param minRssi Minimum RSSI threshold.
+ * @param rssiEventHandler Called when RSSI goes above maxRssi or below minRssi
+ * @return 0 for success, -1 for failure
+ */
public int startRssiMonitoring(byte maxRssi, byte minRssi,
- WifiRssiEventHandler rssiEventHandler) {
- Log.d(TAG, "startRssiMonitoring: maxRssi=" + maxRssi + " minRssi=" + minRssi);
- synchronized (sLock) {
- sWifiRssiEventHandler = rssiEventHandler;
- if (isHalStarted()) {
- if (sRssiMonitorCmdId != 0) {
- stopRssiMonitoring();
- }
-
- sRssiMonitorCmdId = getNewCmdIdLocked();
- Log.d(TAG, "sRssiMonitorCmdId = " + sRssiMonitorCmdId);
- int ret = startRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId,
- maxRssi, minRssi);
- if (ret != 0) { // if not success
- sRssiMonitorCmdId = 0;
- }
- return ret;
- } else {
- return -1;
- }
- }
+ WifiRssiEventHandler rssiEventHandler) {
+ return mWifiVendorHal.startRssiMonitoring(maxRssi, minRssi, rssiEventHandler);
}
- private native static int stopRssiMonitoringNative(int iface, int idx);
-
public int stopRssiMonitoring() {
- Log.d(TAG, "stopRssiMonitoring, cmdId " + sRssiMonitorCmdId);
- synchronized (sLock) {
- if (isHalStarted()) {
- int ret = 0;
- if (sRssiMonitorCmdId != 0) {
- ret = stopRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId);
- }
- sRssiMonitorCmdId = 0;
- return ret;
- } else {
- return -1;
- }
- }
+ return mWifiVendorHal.stopRssiMonitoring();
}
- private static native WifiWakeReasonAndCounts getWlanWakeReasonCountNative(int iface);
-
/**
* Fetch the host wakeup reasons stats from wlan driver.
+ *
* @return the |WifiWakeReasonAndCounts| object retrieved from the wlan driver.
*/
public WifiWakeReasonAndCounts getWlanWakeReasonCount() {
- Log.d(TAG, "getWlanWakeReasonCount " + sWlan0Index);
- synchronized (sLock) {
- if (isHalStarted()) {
- return getWlanWakeReasonCountNative(sWlan0Index);
- } else {
- return null;
- }
- }
+ return mWifiVendorHal.getWlanWakeReasonCount();
}
- private static native int configureNeighborDiscoveryOffload(int iface, boolean enabled);
-
+ /**
+ * Enable/Disable Neighbour discovery offload functionality in the firmware.
+ *
+ * @param enabled true to enable, false to disable.
+ * @return true for success, false otherwise.
+ */
public boolean configureNeighborDiscoveryOffload(boolean enabled) {
- final String logMsg = "configureNeighborDiscoveryOffload(" + enabled + ")";
- Log.d(mTAG, logMsg);
- synchronized (sLock) {
- if (isHalStarted()) {
- final int ret = configureNeighborDiscoveryOffload(sWlan0Index, enabled);
- if (ret != 0) {
- Log.d(mTAG, logMsg + " returned: " + ret);
- }
- return (ret == 0);
+ return mWifiVendorHal.configureNeighborDiscoveryOffload(enabled);
+ }
+
+ // Firmware roaming control.
+
+ /**
+ * Class to retrieve firmware roaming capability parameters.
+ */
+ public static class RoamingCapabilities {
+ public int maxBlacklistSize;
+ public int maxWhitelistSize;
+ }
+
+ /**
+ * Query the firmware roaming capabilities.
+ * @return true for success, false otherwise.
+ */
+ public boolean getRoamingCapabilities(RoamingCapabilities capabilities) {
+ return mWifiVendorHal.getRoamingCapabilities(capabilities);
+ }
+
+ /**
+ * Macros for controlling firmware roaming.
+ */
+ public static final int DISABLE_FIRMWARE_ROAMING = 0;
+ public static final int ENABLE_FIRMWARE_ROAMING = 1;
+
+ /**
+ * Enable/disable firmware roaming.
+ *
+ * @return error code returned from HAL.
+ */
+ public int enableFirmwareRoaming(int state) {
+ return mWifiVendorHal.enableFirmwareRoaming(state);
+ }
+
+ /**
+ * Class for specifying the roaming configurations.
+ */
+ public static class RoamingConfig {
+ public ArrayList<String> blacklistBssids;
+ public ArrayList<String> whitelistSsids;
+ }
+
+ /**
+ * Set firmware roaming configurations.
+ */
+ public boolean configureRoaming(RoamingConfig config) {
+ Log.d(mTAG, "configureRoaming ");
+ return mWifiVendorHal.configureRoaming(config);
+ }
+
+ /**
+ * Reset firmware roaming configuration.
+ */
+ public boolean resetRoamingConfiguration() {
+ // Pass in an empty RoamingConfig object which translates to zero size
+ // blacklist and whitelist to reset the firmware roaming configuration.
+ return mWifiVendorHal.configureRoaming(new RoamingConfig());
+ }
+
+ /********************************************************
+ * JNI operations
+ ********************************************************/
+ /* Register native functions */
+ static {
+ /* Native functions are defined in libwifi-service.so */
+ System.loadLibrary("wifi-service");
+ registerNatives();
+ }
+
+ private static native int registerNatives();
+ /* kernel logging support */
+ private static native byte[] readKernelLogNative();
+
+ /**
+ * Fetches the latest kernel logs.
+ */
+ public synchronized String readKernelLog() {
+ byte[] bytes = readKernelLogNative();
+ if (bytes != null) {
+ CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
+ try {
+ CharBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
+ return decoded.toString();
+ } catch (CharacterCodingException cce) {
+ return new String(bytes, StandardCharsets.ISO_8859_1);
}
+ } else {
+ return "*** failed to read kernel log ***";
}
- return false;
}
}
diff --git a/service/java/com/android/server/wifi/WifiNetworkHistory.java b/service/java/com/android/server/wifi/WifiNetworkHistory.java
index edbc516..f8457cd 100644
--- a/service/java/com/android/server/wifi/WifiNetworkHistory.java
+++ b/service/java/com/android/server/wifi/WifiNetworkHistory.java
@@ -27,7 +27,6 @@
import android.os.Process;
import android.text.TextUtils;
-import android.util.LocalLog;
import android.util.Log;
import com.android.server.net.DelayedDiskWrite;
@@ -110,17 +109,15 @@
protected final DelayedDiskWrite mWriter;
Context mContext;
- private final LocalLog mLocalLog;
/*
* Lost config list, whenever we read a config from networkHistory.txt that was not in
* wpa_supplicant.conf
*/
HashSet<String> mLostConfigsDbg = new HashSet<String>();
- public WifiNetworkHistory(Context c, LocalLog localLog, DelayedDiskWrite writer) {
+ public WifiNetworkHistory(Context c, DelayedDiskWrite writer) {
mContext = c;
mWriter = writer;
- mLocalLog = localLog;
}
/**
@@ -323,9 +320,8 @@
* information read from wpa_supplicant.conf
*/
public void readNetworkHistory(Map<String, WifiConfiguration> configs,
- ConcurrentHashMap<Integer, ScanDetailCache> scanDetailCaches,
+ Map<Integer, ScanDetailCache> scanDetailCaches,
Set<String> deletedEphemeralSSIDs) {
- localLog("readNetworkHistory() path:" + NETWORK_HISTORY_CONFIG_FILE);
try (DataInputStream in =
new DataInputStream(new BufferedInputStream(
@@ -360,7 +356,7 @@
// skip reading that configuration data
// since we don't have a corresponding network ID
if (config == null) {
- localLog("readNetworkHistory didnt find netid for hash="
+ Log.e(TAG, "readNetworkHistory didnt find netid for hash="
+ Integer.toString(value.hashCode())
+ " key: " + value);
mLostConfigsDbg.add(value);
@@ -622,18 +618,15 @@
}
}
- private void localLog(String s) {
- if (mLocalLog != null) {
- mLocalLog.log(s);
- }
- }
-
private ScanDetailCache getScanDetailCache(WifiConfiguration config,
- ConcurrentHashMap<Integer, ScanDetailCache> scanDetailCaches) {
+ Map<Integer, ScanDetailCache> scanDetailCaches) {
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.SCAN_CACHE_ENTRIES_MAX_SIZE,
+ WifiConfigManager.SCAN_CACHE_ENTRIES_TRIM_SIZE);
scanDetailCaches.put(config.networkId, cache);
}
return cache;
diff --git a/service/java/com/android/server/wifi/WifiNetworkScoreCache.java b/service/java/com/android/server/wifi/WifiNetworkScoreCache.java
deleted file mode 100644
index ed37935..0000000
--- a/service/java/com/android/server/wifi/WifiNetworkScoreCache.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2014 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.Manifest.permission;
-import android.content.Context;
-import android.net.INetworkScoreCache;
-import android.net.NetworkKey;
-import android.net.ScoredNetwork;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiManager;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class WifiNetworkScoreCache extends INetworkScoreCache.Stub {
- private static final String TAG = "WifiNetworkScoreCache";
- private static final boolean DBG = false;
-
- // A Network scorer returns a score in the range [-128, +127]
- // We treat the lowest possible score as though there were no score, effectively allowing the
- // scorer to provide an RSSI threshold below which a network should not be used.
- public static final int INVALID_NETWORK_SCORE = Byte.MIN_VALUE;
- private final Context mContext;
-
- // The key is of the form "<ssid>"<bssid>
- // TODO: What about SSIDs that can't be encoded as UTF-8?
- private final Map<String, ScoredNetwork> mNetworkCache;
-
- public WifiNetworkScoreCache(Context context) {
- mContext = context;
- mNetworkCache = new HashMap<String, ScoredNetwork>();
- }
-
- @Override public final void updateScores(List<android.net.ScoredNetwork> networks) {
- if (networks == null) {
- return;
- }
- Log.e(TAG, "updateScores list size=" + networks.size());
-
- synchronized(mNetworkCache) {
- for (ScoredNetwork network : networks) {
- String networkKey = buildNetworkKey(network);
- if (networkKey == null) continue;
- mNetworkCache.put(networkKey, network);
- }
- }
- }
-
- @Override public final void clearScores() {
- synchronized (mNetworkCache) {
- mNetworkCache.clear();
- }
- }
-
- /**
- * Returns whether there is any score info for the given ScanResult.
- *
- * This includes null-score info, so it should only be used when determining whether to request
- * scores from the network scorer.
- */
- public boolean isScoredNetwork(ScanResult result) {
- return getScoredNetwork(result) != null;
- }
-
- /**
- * Returns whether there is a non-null score curve for the given ScanResult.
- *
- * A null score curve has special meaning - we should never connect to an ephemeral network if
- * the score curve is null.
- */
- public boolean hasScoreCurve(ScanResult result) {
- ScoredNetwork network = getScoredNetwork(result);
- return network != null && network.rssiCurve != null;
- }
-
- public int getNetworkScore(ScanResult result) {
-
- int score = INVALID_NETWORK_SCORE;
-
- ScoredNetwork network = getScoredNetwork(result);
- if (network != null && network.rssiCurve != null) {
- score = network.rssiCurve.lookupScore(result.level);
- if (DBG) {
- Log.e(TAG, "getNetworkScore found scored network " + network.networkKey
- + " score " + Integer.toString(score)
- + " RSSI " + result.level);
- }
- }
- return score;
- }
-
- /**
- * Returns the ScoredNetwork metered hint for a given ScanResult.
- *
- * If there is no ScoredNetwork associated with the ScanResult then false will be returned.
- */
- public boolean getMeteredHint(ScanResult result) {
- ScoredNetwork network = getScoredNetwork(result);
- return network != null && network.meteredHint;
- }
-
- public int getNetworkScore(ScanResult result, boolean isActiveNetwork) {
-
- int score = INVALID_NETWORK_SCORE;
-
- ScoredNetwork network = getScoredNetwork(result);
- if (network != null && network.rssiCurve != null) {
- score = network.rssiCurve.lookupScore(result.level, isActiveNetwork);
- if (DBG) {
- Log.e(TAG, "getNetworkScore found scored network " + network.networkKey
- + " score " + Integer.toString(score)
- + " RSSI " + result.level
- + " isActiveNetwork " + isActiveNetwork);
- }
- }
- return score;
- }
-
- private ScoredNetwork getScoredNetwork(ScanResult result) {
- String key = buildNetworkKey(result);
- if (key == null) return null;
-
- //find it
- synchronized(mNetworkCache) {
- ScoredNetwork network = mNetworkCache.get(key);
- return network;
- }
- }
-
- private String buildNetworkKey(ScoredNetwork network) {
- if (network == null || network.networkKey == null) return null;
- if (network.networkKey.wifiKey == null) return null;
- if (network.networkKey.type == NetworkKey.TYPE_WIFI) {
- String key = network.networkKey.wifiKey.ssid;
- if (key == null) return null;
- if (network.networkKey.wifiKey.bssid != null) {
- key = key + network.networkKey.wifiKey.bssid;
- }
- return key;
- }
- return null;
- }
-
- private String buildNetworkKey(ScanResult result) {
- if (result == null || result.SSID == null) {
- return null;
- }
- StringBuilder key = new StringBuilder("\"");
- key.append(result.SSID);
- key.append("\"");
- if (result.BSSID != null) {
- key.append(result.BSSID);
- }
- return key.toString();
- }
-
- @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
- writer.println("WifiNetworkScoreCache");
- writer.println(" All score curves:");
- for (Map.Entry<String, ScoredNetwork> entry : mNetworkCache.entrySet()) {
- ScoredNetwork scoredNetwork = entry.getValue();
- writer.println(" " + entry.getKey() + ": " + scoredNetwork.rssiCurve
- + ", meteredHint=" + scoredNetwork.meteredHint);
- }
- writer.println(" Current network scores:");
- WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
- for (ScanResult scanResult : wifiManager.getScanResults()) {
- writer.println(" " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult));
- }
- }
-
-}
diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java
new file mode 100644
index 0000000..ce04bd2
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java
@@ -0,0 +1,531 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.NetworkKey;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Pair;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * This class looks at all the connectivity scan results then
+ * selects a network for the phone to connect or roam to.
+ */
+public class WifiNetworkSelector {
+ private static final long INVALID_TIME_STAMP = Long.MIN_VALUE;
+ // Minimum time gap between last successful network selection and a new selection
+ // attempt.
+ @VisibleForTesting
+ public static final int MINIMUM_NETWORK_SELECTION_INTERVAL_MS = 10 * 1000;
+
+ private final WifiConfigManager mWifiConfigManager;
+ private final Clock mClock;
+ private final LocalLog mLocalLog;
+ private long mLastNetworkSelectionTimeStamp = INVALID_TIME_STAMP;
+ // Buffer of filtered scan results (Scan results considered by network selection) & associated
+ // WifiConfiguration (if any).
+ private volatile List<Pair<ScanDetail, WifiConfiguration>> mConnectableNetworks =
+ new ArrayList<>();
+ private final int mThresholdQualifiedRssi24;
+ private final int mThresholdQualifiedRssi5;
+ private final int mThresholdMinimumRssi24;
+ private final int mThresholdMinimumRssi5;
+ private final boolean mEnableAutoJoinWhenAssociated;
+
+ /**
+ * WiFi Network Selector supports various types of networks. Each type can
+ * have its evaluator to choose the best WiFi network for the device to connect
+ * to. When registering a WiFi network evaluator with the WiFi Network Selector,
+ * the priority of the network must be specified, and it must be a value between
+ * 0 and (EVALUATOR_MIN_PIRORITY - 1) with 0 being the highest priority. Wifi
+ * Network Selector iterates through the registered scorers from the highest priority
+ * to the lowest till a network is selected.
+ */
+ public static final int EVALUATOR_MIN_PRIORITY = 6;
+
+ /**
+ * Maximum number of evaluators can be registered with Wifi Network Selector.
+ */
+ public static final int MAX_NUM_EVALUATORS = EVALUATOR_MIN_PRIORITY;
+
+ /**
+ * Interface for WiFi Network Evaluator
+ *
+ * A network scorer evaulates all the networks from the scan results and
+ * recommends the best network in its category to connect or roam to.
+ */
+ public interface NetworkEvaluator {
+ /**
+ * Get the evaluator name.
+ */
+ String getName();
+
+ /**
+ * Update the evaluator.
+ *
+ * Certain evaluators have to be updated with the new scan results. For example
+ * the ExternalScoreEvalutor needs to refresh its Score Cache.
+ *
+ * @param scanDetails a list of scan details constructed from the scan results
+ */
+ void update(List<ScanDetail> scanDetails);
+
+ /**
+ * Evaluate all the networks from the scan results.
+ *
+ * @param scanDetails a list of scan details constructed from the scan results
+ * @param currentNetwork configuration of the current connected network
+ * or null if disconnected
+ * @param currentBssid BSSID of the current connected network or null if
+ * disconnected
+ * @param connected a flag to indicate if WifiStateMachine is in connected
+ * state
+ * @param untrustedNetworkAllowed a flag to indidate if untrusted networks like
+ * ephemeral networks are allowed
+ * @param connectableNetworks a list of the ScanDetail and WifiConfiguration
+ * pair which is used by the WifiLastResortWatchdog
+ * @return configuration of the chosen network;
+ * null if no network in this category is available.
+ */
+ @Nullable
+ WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
+ WifiConfiguration currentNetwork, String currentBssid,
+ boolean connected, boolean untrustedNetworkAllowed,
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks);
+ }
+
+ private final NetworkEvaluator[] mEvaluators = new NetworkEvaluator[MAX_NUM_EVALUATORS];
+
+ // A helper to log debugging information in the local log buffer, which can
+ // be retrieved in bugreport.
+ private void localLog(String log) {
+ mLocalLog.log(log);
+ }
+
+ private boolean isCurrentNetworkSufficient(WifiInfo wifiInfo) {
+ WifiConfiguration network =
+ mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
+
+ // Currently connected?
+ if (network == null) {
+ localLog("No current connected network.");
+ return false;
+ } else {
+ localLog("Current connected network: " + network.SSID
+ + " , ID: " + network.networkId);
+ }
+
+ // Ephemeral network is not qualified.
+ if (network.ephemeral) {
+ localLog("Current network is an ephemeral one.");
+ return false;
+ }
+
+ // Open network is not qualified.
+ if (WifiConfigurationUtil.isConfigForOpenNetwork(network)) {
+ localLog("Current network is a open one.");
+ return false;
+ }
+
+ // 2.4GHz networks is not qualified.
+ if (wifiInfo.is24GHz()) {
+ localLog("Current network is 2.4GHz.");
+ return false;
+ }
+
+ // Is the current network's singnal strength qualified? It can only
+ // be a 5GHz network if we reach here.
+ int currentRssi = wifiInfo.getRssi();
+ if (wifiInfo.is5GHz() && currentRssi < mThresholdQualifiedRssi5) {
+ localLog("Current network band=" + (wifiInfo.is5GHz() ? "5GHz" : "2.4GHz")
+ + ", RSSI[" + currentRssi + "]-acceptable but not qualified.");
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean isNetworkSelectionNeeded(List<ScanDetail> scanDetails, WifiInfo wifiInfo,
+ boolean connected, boolean disconnected) {
+ if (scanDetails.size() == 0) {
+ localLog("Empty connectivity scan results. Skip network selection.");
+ return false;
+ }
+
+ if (connected) {
+ // Is roaming allowed?
+ if (!mEnableAutoJoinWhenAssociated) {
+ localLog("Switching networks in connected state is not allowed."
+ + " Skip network selection.");
+ return false;
+ }
+
+ // Has it been at least the minimum interval since last network selection?
+ if (mLastNetworkSelectionTimeStamp != INVALID_TIME_STAMP) {
+ long gap = mClock.getElapsedSinceBootMillis()
+ - mLastNetworkSelectionTimeStamp;
+ if (gap < MINIMUM_NETWORK_SELECTION_INTERVAL_MS) {
+ localLog("Too short since last network selection: " + gap + " ms."
+ + " Skip network selection.");
+ return false;
+ }
+ }
+
+ if (isCurrentNetworkSufficient(wifiInfo)) {
+ localLog("Current connected network already sufficient. Skip network selection.");
+ return false;
+ } else {
+ localLog("Current connected network is not sufficient.");
+ return true;
+ }
+ } else if (disconnected) {
+ return true;
+ } else {
+ // No network selection if WifiStateMachine is in a state other than
+ // CONNECTED or DISCONNECTED.
+ localLog("WifiStateMachine is in neither CONNECTED nor DISCONNECTED state."
+ + " Skip network selection.");
+ return false;
+ }
+ }
+
+ /**
+ * Format the given ScanResult as a scan ID for logging.
+ */
+ public static String toScanId(@Nullable ScanResult scanResult) {
+ return scanResult == null ? "NULL"
+ : String.format("%s:%s", scanResult.SSID, scanResult.BSSID);
+ }
+
+ /**
+ * Format the given WifiConfiguration as a SSID:netId string
+ */
+ public static String toNetworkString(WifiConfiguration network) {
+ if (network == null) {
+ return null;
+ }
+
+ return (network.SSID + ":" + network.networkId);
+ }
+
+ private List<ScanDetail> filterScanResults(List<ScanDetail> scanDetails,
+ HashSet<String> bssidBlacklist, boolean isConnected, String currentBssid) {
+ ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>();
+ List<ScanDetail> validScanDetails = new ArrayList<ScanDetail>();
+ StringBuffer noValidSsid = new StringBuffer();
+ StringBuffer blacklistedBssid = new StringBuffer();
+ StringBuffer lowRssi = new StringBuffer();
+ boolean scanResultsHaveCurrentBssid = false;
+
+ for (ScanDetail scanDetail : scanDetails) {
+ ScanResult scanResult = scanDetail.getScanResult();
+
+ if (TextUtils.isEmpty(scanResult.SSID)) {
+ noValidSsid.append(scanResult.BSSID).append(" / ");
+ continue;
+ }
+
+ // Check if the scan results contain the currently connected BSSID
+ if (scanResult.BSSID.equals(currentBssid)) {
+ scanResultsHaveCurrentBssid = true;
+ }
+
+ final String scanId = toScanId(scanResult);
+
+ if (bssidBlacklist.contains(scanResult.BSSID)) {
+ blacklistedBssid.append(scanId).append(" / ");
+ continue;
+ }
+
+ // Skip network with too weak signals.
+ if ((scanResult.is24GHz() && scanResult.level
+ < mThresholdMinimumRssi24)
+ || (scanResult.is5GHz() && scanResult.level
+ < mThresholdMinimumRssi5)) {
+ lowRssi.append(scanId).append("(")
+ .append(scanResult.is24GHz() ? "2.4GHz" : "5GHz")
+ .append(")").append(scanResult.level).append(" / ");
+ continue;
+ }
+
+ validScanDetails.add(scanDetail);
+ }
+
+ // WNS listens to all single scan results. Some scan requests may not include
+ // the channel of the currently connected network, so the currently connected
+ // network won't show up in the scan results. We don't act on these scan results
+ // to avoid aggressive network switching which might trigger disconnection.
+ if (isConnected && !scanResultsHaveCurrentBssid) {
+ localLog("Current connected BSSID " + currentBssid + " is not in the scan results."
+ + " Skip network selection.");
+ validScanDetails.clear();
+ return validScanDetails;
+ }
+
+ if (noValidSsid.length() != 0) {
+ localLog("Networks filtered out due to invalid SSID: " + noValidSsid);
+ }
+
+ if (blacklistedBssid.length() != 0) {
+ localLog("Networks filtered out due to blacklist: " + blacklistedBssid);
+ }
+
+ if (lowRssi.length() != 0) {
+ localLog("Networks filtered out due to low signal strength: " + lowRssi);
+ }
+
+ return validScanDetails;
+ }
+
+ /**
+ * @return the list of ScanDetails scored as potential candidates by the last run of
+ * selectNetwork, this will be empty if Network selector determined no selection was
+ * needed on last run. This includes scan details of sufficient signal strength, and
+ * had an associated WifiConfiguration.
+ */
+ public List<Pair<ScanDetail, WifiConfiguration>> getFilteredScanDetails() {
+ return mConnectableNetworks;
+ }
+
+ /**
+ * This API is called when user explicitly selects a network. Currently, it is used in following
+ * cases:
+ * (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. This network is favored over all the other networks visible in latest network
+ * selection procedure.
+ *
+ * @param netId ID for the network chosen by the user
+ * @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 setUserConnectChoice(int netId) {
+ localLog("userSelectNetwork: network ID=" + netId);
+ WifiConfiguration selected = mWifiConfigManager.getConfiguredNetwork(netId);
+
+ if (selected == null || selected.SSID == null) {
+ localLog("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);
+ }
+
+ boolean change = false;
+ String key = selected.configKey();
+ // This is only used for setting the connect choice timestamp for debugging purposes.
+ long currentTime = mClock.getWallClockMillis();
+ List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks();
+
+ for (WifiConfiguration network : savedNetworks) {
+ WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus();
+ if (network.networkId == selected.networkId) {
+ if (status.getConnectChoice() != null) {
+ localLog("Remove user selection preference of " + status.getConnectChoice()
+ + " Set Time: " + status.getConnectChoiceTimestamp() + " from "
+ + network.SSID + " : " + network.networkId);
+ mWifiConfigManager.clearNetworkConnectChoice(network.networkId);
+ change = true;
+ }
+ continue;
+ }
+
+ if (status.getSeenInLastQualifiedNetworkSelection()
+ && (status.getConnectChoice() == null
+ || !status.getConnectChoice().equals(key))) {
+ localLog("Add key: " + key + " Set Time: " + currentTime + " to "
+ + toNetworkString(network));
+ mWifiConfigManager.setNetworkConnectChoice(network.networkId, key, currentTime);
+ change = true;
+ }
+ }
+
+ return change;
+ }
+
+ /**
+ * Overrides the {@code candidate} chosen by the {@link #mEvaluators} with the user chosen
+ * {@link WifiConfiguration} if one exists.
+ *
+ * @return the user chosen {@link WifiConfiguration} if one exists, {@code candidate} otherwise
+ */
+ private WifiConfiguration overrideCandidateWithUserConnectChoice(
+ @NonNull WifiConfiguration candidate) {
+ WifiConfiguration tempConfig = candidate;
+ WifiConfiguration originalCandidate = candidate;
+ ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
+
+ while (tempConfig.getNetworkSelectionStatus().getConnectChoice() != null) {
+ String key = tempConfig.getNetworkSelectionStatus().getConnectChoice();
+ tempConfig = mWifiConfigManager.getConfiguredNetwork(key);
+
+ if (tempConfig != null) {
+ WifiConfiguration.NetworkSelectionStatus tempStatus =
+ tempConfig.getNetworkSelectionStatus();
+ if (tempStatus.getCandidate() != null && tempStatus.isNetworkEnabled()) {
+ scanResultCandidate = tempStatus.getCandidate();
+ candidate = tempConfig;
+ }
+ } else {
+ localLog("Connect choice: " + key + " has no corresponding saved config.");
+ break;
+ }
+ }
+
+ if (candidate != originalCandidate) {
+ localLog("After user selection adjustment, the final candidate is:"
+ + WifiNetworkSelector.toNetworkString(candidate) + " : "
+ + scanResultCandidate.BSSID);
+ }
+ return candidate;
+ }
+
+ /**
+ * Select the best network from the ones in range.
+ *
+ * @param scanDetails List of ScanDetail for all the APs in range
+ * @param bssidBlacklist Blacklisted BSSIDs
+ * @param wifiInfo Currently connected network
+ * @param connected True if the device is connected
+ * @param disconnected True if the device is disconnected
+ * @param untrustedNetworkAllowed True if untrusted networks are allowed for connection
+ * @return Configuration of the selected network, or Null if nothing
+ */
+ @Nullable
+ public WifiConfiguration selectNetwork(List<ScanDetail> scanDetails,
+ HashSet<String> bssidBlacklist, WifiInfo wifiInfo,
+ boolean connected, boolean disconnected, boolean untrustedNetworkAllowed) {
+ mConnectableNetworks.clear();
+ if (scanDetails.size() == 0) {
+ localLog("Empty connectivity scan result");
+ return null;
+ }
+
+ WifiConfiguration currentNetwork =
+ mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
+
+ // Always get the current BSSID from WifiInfo in case that firmware initiated
+ // roaming happened.
+ String currentBssid = wifiInfo.getBSSID();
+
+ // Shall we start network selection at all?
+ if (!isNetworkSelectionNeeded(scanDetails, wifiInfo, connected, disconnected)) {
+ return null;
+ }
+
+ // Update the registered network evaluators.
+ for (NetworkEvaluator registeredEvaluator : mEvaluators) {
+ if (registeredEvaluator != null) {
+ registeredEvaluator.update(scanDetails);
+ }
+ }
+
+ // Filter out unwanted networks.
+ List<ScanDetail> filteredScanDetails = filterScanResults(scanDetails, bssidBlacklist,
+ connected, currentBssid);
+ if (filteredScanDetails.size() == 0) {
+ return null;
+ }
+
+ // Go through the registered network evaluators from the highest priority
+ // one to the lowest till a network is selected.
+ WifiConfiguration selectedNetwork = null;
+ for (NetworkEvaluator registeredEvaluator : mEvaluators) {
+ if (registeredEvaluator != null) {
+ localLog("About to run " + registeredEvaluator.getName() + " :");
+ selectedNetwork = registeredEvaluator.evaluateNetworks(filteredScanDetails,
+ currentNetwork, currentBssid, connected,
+ untrustedNetworkAllowed, mConnectableNetworks);
+ if (selectedNetwork != null) {
+ localLog(registeredEvaluator.getName() + " selects "
+ + WifiNetworkSelector.toNetworkString(selectedNetwork) + " : "
+ + selectedNetwork.getNetworkSelectionStatus().getCandidate().BSSID);
+ break;
+ }
+ }
+ }
+
+ if (selectedNetwork != null) {
+ selectedNetwork = overrideCandidateWithUserConnectChoice(selectedNetwork);
+ mLastNetworkSelectionTimeStamp = mClock.getElapsedSinceBootMillis();
+ }
+
+ return selectedNetwork;
+ }
+
+ /**
+ * Register a network evaluator
+ *
+ * @param evaluator the network evaluator to be registered
+ * @param priority a value between 0 and (SCORER_MIN_PRIORITY-1)
+ *
+ * @return true if the evaluator is successfully registered with QNS;
+ * false if failed to register the evaluator
+ */
+ public boolean registerNetworkEvaluator(NetworkEvaluator evaluator, int priority) {
+ if (priority < 0 || priority >= EVALUATOR_MIN_PRIORITY) {
+ localLog("Invalid network evaluator priority: " + priority);
+ return false;
+ }
+
+ if (mEvaluators[priority] != null) {
+ localLog("Priority " + priority + " is already registered by "
+ + mEvaluators[priority].getName());
+ return false;
+ }
+
+ mEvaluators[priority] = evaluator;
+ return true;
+ }
+
+ WifiNetworkSelector(Context context, WifiConfigManager configManager, Clock clock,
+ LocalLog localLog) {
+ mWifiConfigManager = configManager;
+ mClock = clock;
+ mLocalLog = localLog;
+
+ mThresholdQualifiedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
+ mThresholdQualifiedRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
+ mThresholdMinimumRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
+ mThresholdMinimumRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
+ mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
+ R.bool.config_wifi_framework_enable_associated_network_selection);
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiNotificationController.java b/service/java/com/android/server/wifi/WifiNotificationController.java
index 6df2eb8..c8e5e90 100644
--- a/service/java/com/android/server/wifi/WifiNotificationController.java
+++ b/service/java/com/android/server/wifi/WifiNotificationController.java
@@ -20,7 +20,6 @@
import android.app.NotificationManager;
import android.app.TaskStackBuilder;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -28,18 +27,25 @@
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
+import com.android.internal.notification.SystemNotificationChannels;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
-/* Takes care of handling the "open wi-fi network available" notification @hide */
-final class WifiNotificationController {
+/**
+ * Takes care of handling the "open wi-fi network available" notification
+ * @hide
+ */
+public class WifiNotificationController {
/**
* The icon to show in the 'available networks' notification. This will also
* be the ID of the Notification given to the NotificationManager.
@@ -50,6 +56,7 @@
* When a notification is shown, we wait this amount before possibly showing it again.
*/
private final long NOTIFICATION_REPEAT_DELAY_MS;
+
/**
* Whether the user has set the setting to show the 'available networks' notification.
*/
@@ -58,6 +65,7 @@
* Observes the user setting to keep {@link #mNotificationEnabled} in sync.
*/
private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
+
/**
* The {@link System#currentTimeMillis()} must be at least this value for us
* to show the notification again.
@@ -89,18 +97,22 @@
private int mNumScansSinceNetworkStateChange;
private final Context mContext;
- private final WifiStateMachine mWifiStateMachine;
private NetworkInfo mNetworkInfo;
private NetworkInfo.DetailedState mDetailedState;
private volatile int mWifiState;
private FrameworkFacade mFrameworkFacade;
+ private WifiInjector mWifiInjector;
+ private WifiScanner mWifiScanner;
- WifiNotificationController(Context context, Looper looper, WifiStateMachine wsm,
- FrameworkFacade framework, Notification.Builder builder) {
+ WifiNotificationController(Context context,
+ Looper looper,
+ FrameworkFacade framework,
+ Notification.Builder builder,
+ WifiInjector wifiInjector) {
mContext = context;
- mWifiStateMachine = wsm;
mFrameworkFacade = framework;
mNotificationBuilder = builder;
+ mWifiInjector = wifiInjector;
mWifiState = WifiManager.WIFI_STATE_UNKNOWN;
mDetailedState = NetworkInfo.DetailedState.IDLE;
@@ -113,7 +125,8 @@
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ if (intent.getAction()
+ .equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN);
resetNotification();
@@ -133,19 +146,33 @@
case CAPTIVE_PORTAL_CHECK:
resetNotification();
break;
+
+ case IDLE:
+ case SCANNING:
+ case CONNECTING:
+ case AUTHENTICATING:
+ case OBTAINING_IPADDR:
+ case SUSPENDED:
+ case FAILED:
+ case BLOCKED:
+ case VERIFYING_POOR_LINK:
+ break;
}
}
} else if (intent.getAction().equals(
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ if (mWifiScanner == null) {
+ mWifiScanner = mWifiInjector.getWifiScanner();
+ }
checkAndSetNotification(mNetworkInfo,
- mWifiStateMachine.syncGetScanResultsList());
+ mWifiScanner.getSingleScanResults());
}
}
}, filter);
// Setting is in seconds
NOTIFICATION_REPEAT_DELAY_MS = mFrameworkFacade.getIntegerSetting(context,
- Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
+ Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000L;
mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(
new Handler(looper));
mNotificationEnabledSettingObserver.register();
@@ -159,6 +186,10 @@
// don't bother doing any of the following
if (!mNotificationEnabled) return;
if (mWifiState != WifiManager.WIFI_STATE_ENABLED) return;
+ if (UserManager.get(mContext)
+ .hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) {
+ return;
+ }
NetworkInfo.State state = NetworkInfo.State.DISCONNECTED;
if (networkInfo != null)
@@ -245,7 +276,8 @@
if (mNotificationBuilder == null) {
// Cache the Notification builder object.
- mNotificationBuilder = new Notification.Builder(mContext)
+ mNotificationBuilder = new Notification.Builder(mContext,
+ SystemNotificationChannels.NETWORK_AVAILABLE)
.setWhen(0)
.setSmallIcon(ICON_NETWORKS_AVAILABLE)
.setAutoCancel(true)
@@ -289,8 +321,7 @@
}
public void register() {
- ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(Settings.Global.getUriFor(
+ mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor(
Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
synchronized (WifiNotificationController.this) {
mNotificationEnabled = getValue();
diff --git a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java b/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
deleted file mode 100644
index 1e75603..0000000
--- a/service/java/com/android/server/wifi/WifiQualifiedNetworkSelector.java
+++ /dev/null
@@ -1,1313 +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 android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.NetworkKey;
-import android.net.NetworkScoreManager;
-import android.net.WifiKey;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiEnterpriseConfig;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.LocalLog;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wifi.util.ScanDetailUtil;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.nio.charset.StandardCharsets;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This class looks at all the connectivity scan results then
- * select an network for the phone to connect/roam to.
- */
-public class WifiQualifiedNetworkSelector {
- private WifiConfigManager mWifiConfigManager;
- private WifiInfo mWifiInfo;
- private NetworkScoreManager mScoreManager;
- private WifiNetworkScoreCache mNetworkScoreCache;
- private Clock mClock;
- private static final String TAG = "WifiQualifiedNetworkSelector:";
- // Always enable debugging logs for now since QNS is still a new feature.
- private static final boolean FORCE_DEBUG = true;
- 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)
- 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;
-
- private static final int CARRIER_SSID = 0;
- private static final int CARRIER_KEY = 1;
- private static final int CARRIER_EAP_METHOD = 2;
-
- //if current network is on 2.4GHz band and has a RSSI over this, need not new network selection
- 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
- public static final int QUALIFIED_RSSI_5G_BAND = -70;
- //any RSSI larger than this will benefit the traffic very limited
- 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
- public static final int MINIMUM_2G_ACCEPT_RSSI = -85;
- public static final int MINIMUM_5G_ACCEPT_RSSI = -82;
-
- 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;
- public static final int BSSID_BLACKLIST_THRESHOLD = 3;
- public static final int BSSID_BLACKLIST_EXPIRE_TIME = 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;
-
- private final LocalLog mLocalLog = new LocalLog(512);
- private int mRssiScoreSlope = RSSI_SCORE_SLOPE;
- private int mRssiScoreOffset = RSSI_SCORE_OFFSET;
- private int mSameBssidAward = SAME_BSSID_AWARD;
- private int mLastSelectionAward = LAST_SELECTION_AWARD;
- private int mPasspointSecurityAward = PASSPOINT_SECURITY_AWARD;
- private int mSecurityAward = SECURITY_AWARD;
- private int mUserPreferedBand = WifiManager.WIFI_FREQUENCY_BAND_AUTO;
- private Map<String, BssidBlacklistStatus> mBssidBlacklist =
- new HashMap<String, BssidBlacklistStatus>();
- private List<WifiConfiguration> mCarrierConfiguredNetworks = new ArrayList<WifiConfiguration>();
- private Context mContext;
-
- /**
- * class save the blacklist status of a given BSSID
- */
- private static class BssidBlacklistStatus {
- //how many times it is requested to be blacklisted (association rejection trigger this)
- int mCounter;
- boolean mIsBlacklisted;
- long mBlacklistedTimeStamp = INVALID_TIME_STAMP;
- }
-
- private void localLog(String log) {
- if (mDbg) {
- mLocalLog.log(log);
- }
- }
-
- @VisibleForTesting
- public void setCarrierConfiguredNetworks(List<WifiConfiguration> carrierConfiguredNetworks) {
- mCarrierConfiguredNetworks = carrierConfiguredNetworks;
- }
-
- private void localLoge(String log) {
- mLocalLog.log(log);
- }
-
- @VisibleForTesting
- void setWifiNetworkScoreCache(WifiNetworkScoreCache cache) {
- mNetworkScoreCache = cache;
- }
-
- /**
- * @return current target connected network
- */
- public WifiConfiguration getConnetionTargetNetwork() {
- return mCurrentConnectedNetwork;
- }
-
- /**
- * @return the list of ScanDetails scored as potential candidates by the last run of
- * selectQualifiedNetwork, this will be empty if QNS determined no selection was needed on last
- * run. This includes scan details of sufficient signal strength, and had an associated
- * WifiConfiguration.
- */
- public List<Pair<ScanDetail, WifiConfiguration>> getFilteredScanDetails() {
- return mFilteredScanDetails;
- }
-
- /**
- * set the user selected preferred band
- *
- * @param band preferred band user selected
- */
- public void setUserPreferredBand(int band) {
- mUserPreferedBand = band;
- }
-
- WifiQualifiedNetworkSelector(WifiConfigManager configureStore, Context context,
- WifiInfo wifiInfo, Clock clock) {
- mWifiConfigManager = configureStore;
- mWifiInfo = wifiInfo;
- mClock = clock;
- mContext = context;
- mScoreManager =
- (NetworkScoreManager) context.getSystemService(Context.NETWORK_SCORE_SERVICE);
- if (mScoreManager != null) {
- 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);
- mNetworkScoreCache = null;
- }
-
- mRssiScoreSlope = context.getResources().getInteger(
- R.integer.config_wifi_framework_RSSI_SCORE_SLOPE);
- mRssiScoreOffset = context.getResources().getInteger(
- R.integer.config_wifi_framework_RSSI_SCORE_OFFSET);
- mSameBssidAward = context.getResources().getInteger(
- R.integer.config_wifi_framework_SAME_BSSID_AWARD);
- mLastSelectionAward = context.getResources().getInteger(
- R.integer.config_wifi_framework_LAST_SELECTION_AWARD);
- mPasspointSecurityAward = context.getResources().getInteger(
- R.integer.config_wifi_framework_PASSPOINT_SECURITY_AWARD);
- mSecurityAward = context.getResources().getInteger(
- R.integer.config_wifi_framework_SECURITY_AWARD);
- mNoIntnetPenalty = (mWifiConfigManager.mThresholdSaturatedRssi24.get() + mRssiScoreOffset)
- * mRssiScoreSlope + mWifiConfigManager.mBandAward5Ghz.get()
- + mWifiConfigManager.mCurrentNetworkBoost.get() + mSameBssidAward + mSecurityAward;
-
- context.registerReceiver(mBroadcastReceiver, new IntentFilter(
- CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
- }
-
- @VisibleForTesting
- public List<WifiConfiguration> parseCarrierSuppliedWifiInfo(String[] wifiArray) {
- List<WifiConfiguration> carrierConfiguredNetworks = new ArrayList<WifiConfiguration>();
- for (String config : wifiArray) {
- String[] wc = config.split("\\|");
- if (wc.length != 3) {
- continue;
- }
- WifiConfiguration wifiConfig = new WifiConfiguration();
- try {
- byte[] decodedBytes = Base64.decode(wc[CARRIER_SSID], Base64.DEFAULT);
- String ssid = new String(decodedBytes);
- wifiConfig.SSID = "\"" + ssid + "\"";
- } catch (IllegalArgumentException ex) {
- localLog("mBroadcaseReceiver: Could not decode base64 string");
- continue;
- }
- try {
- int key = Integer.parseInt(wc[CARRIER_KEY]);
- wifiConfig.allowedKeyManagement.set(key);
- int eapType = Integer.parseInt(wc[CARRIER_EAP_METHOD]);
- wifiConfig.enterpriseConfig = new WifiEnterpriseConfig();
- wifiConfig.enterpriseConfig.setEapMethod(eapType);
- } catch (NumberFormatException e) {
- localLog("mBroadcastReceiver: not an integer:" + wc[CARRIER_KEY] + "," +
- wc[CARRIER_EAP_METHOD]);
- continue;
- } catch (IllegalArgumentException e) {
- localLog("mBroadcastReceiver: invalid config" + wc[CARRIER_KEY] + "," +
- wc[CARRIER_EAP_METHOD]);
- }
- carrierConfiguredNetworks.add(wifiConfig);
- localLog("mBroadcastReceiver: carrier config:" + wifiConfig.SSID);
- }
- return carrierConfiguredNetworks;
- }
-
- final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- localLog("mBroadcastReceiver: onReceive " + intent.getAction());
- String[] wifiArray = null;
- CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (carrierConfigManager != null) {
- PersistableBundle b = carrierConfigManager.getConfig();
- if (b != null) {
- wifiArray = b.getStringArray(
- CarrierConfigManager.KEY_CARRIER_WIFI_STRING_ARRAY);
- }
- }
-
- if (wifiArray == null) {
- return;
- }
- mCarrierConfiguredNetworks = parseCarrierSuppliedWifiInfo(wifiArray);
- boolean hasCarrierNetworks = (mCarrierConfiguredNetworks == null ||
- mCarrierConfiguredNetworks.size() == 0) ? false : true;
- mWifiConfigManager.setHasCarrierNetworks(hasCarrierNetworks);
- }
- };
-
- void enableVerboseLogging(int verbose) {
- mDbg = verbose > 0 || FORCE_DEBUG;
- }
-
- private String getNetworkString(WifiConfiguration network) {
- if (network == null) {
- return null;
- }
-
- return (network.SSID + ":" + network.networkId);
-
- }
-
- /**
- * check whether current network is good enough we need not consider any potential switch
- *
- * @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) {
-
- if (currentNetwork == null) {
- localLog("Disconnected");
- return false;
- } else {
- localLog("Current network is: " + currentNetwork.SSID + " ,ID is: "
- + currentNetwork.networkId);
- }
-
- //if current connected network is an ephemeral network,we will consider
- // there is no current network
- if (currentNetwork.ephemeral) {
- localLog("Current is ephemeral. Start reselect");
- return false;
- }
-
- //if current network is open network, not qualified
- if (mWifiConfigManager.isOpenNetwork(currentNetwork)) {
- localLog("Current network is open network");
- return false;
- }
-
- // Current network band must match with user preference selection
- if (mWifiInfo.is24GHz() && (mUserPreferedBand != WifiManager.WIFI_FREQUENCY_BAND_2GHZ)) {
- localLog("Current band does not match user preference. Start Qualified Network"
- + " Selection Current band = " + (mWifiInfo.is24GHz() ? "2.4GHz band"
- : "5GHz band") + "UserPreference band = " + mUserPreferedBand);
- return false;
- }
-
- 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);
- return false;
- }
-
- return true;
- }
-
- /**
- * check whether QualifiedNetworkSelection is needed or not
- *
- * @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 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
- if (isLinkDebouncing) {
- localLog("Need not Qualified Network Selection 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
- if (!mWifiConfigManager.getEnableAutoJoinWhenAssociated()) {
- localLog("Switch network under connection is not allowed");
- return false;
- }
-
- //Do not select again if last selection is within
- //MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL
- 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:"
- + gap + " ms!");
- return false;
- }
- }
-
- WifiConfiguration currentNetwork =
- 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
- return false;
- }
-
- if (!isNetworkQualified(mCurrentConnectedNetwork)) {
- //need not trigger Qualified Network Selection if current network is qualified
- localLog("Current network is not qualified");
- return true;
- } else {
- return false;
- }
- } else if (isDisconnected) {
- mCurrentConnectedNetwork = null;
- mCurrentBssid = null;
- //Do not start Qualified Network Selection if current state is a transient state
- if (isSupplicantTransientState) {
- return false;
- }
- } else {
- //Do not allow new network selection in other state
- localLog("WifiStateMachine is not on connected or disconnected state");
- return false;
- }
-
- return true;
- }
-
- int calculateBssidScore(ScanResult scanResult, WifiConfiguration network,
- WifiConfiguration currentNetwork, boolean sameBssid, boolean sameSelect,
- StringBuffer sbuf) {
-
- int score = 0;
- //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);
- if (scanResult.is5GHz()) {
- //5GHz band
- score += mWifiConfigManager.mBandAward5Ghz.get();
- sbuf.append(" 5GHz bonus: " + mWifiConfigManager.mBandAward5Ghz.get());
- }
-
- //last user selection award
- if (sameSelect) {
- long timeDifference = mClock.elapsedRealtime()
- - 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);
- }
- }
-
- //same network award
- if (network == currentNetwork || network.isLinked(currentNetwork)) {
- score += mWifiConfigManager.mCurrentNetworkBoost.get();
- sbuf.append(" Same network with current associated. Bonus: "
- + mWifiConfigManager.mCurrentNetworkBoost.get());
- }
-
- //same BSSID award
- if (sameBssid) {
- score += mSameBssidAward;
- sbuf.append(" Same BSSID with current association. Bonus: " + mSameBssidAward);
- }
-
- //security award
- if (network.isPasspoint()) {
- score += mPasspointSecurityAward;
- sbuf.append(" Passpoint Bonus:" + mPasspointSecurityAward);
- } else if (!mWifiConfigManager.isOpenNetwork(network)) {
- score += mSecurityAward;
- sbuf.append(" Secure network Bonus:" + 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
- if (network.numNoInternetAccessReports > 0 && !network.validatedInternetAccess) {
- score -= mNoIntnetPenalty;
- sbuf.append(" No internet Penalty:-" + mNoIntnetPenalty);
- }
-
-
- sbuf.append(" Score for scanResult: " + scanResult + " and Network ID: "
- + network.networkId + " final score:" + score + "\n\n");
-
- return score;
- }
-
- /**
- * This API try to update all the saved networks' network selection status
- */
- private void updateSavedNetworkSelectionStatus() {
- List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks();
- if (savedNetworks.size() == 0) {
- localLog("no saved network");
- return;
- }
-
- 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 (status.isNetworkTemporaryDisabled()) {
- mWifiConfigManager.tryEnableQualifiedNetwork(network.networkId);
- }
-
- //clean 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("Connect Choice:" + status.getConnectChoice() + " set time:"
- + status.getConnectChoiceTimestamp());
- sbuf.append("\n");
- }
- localLog(sbuf.toString());
- }
-
- /**
- * This API is called when user explicitly select 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
- * 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
- *
- * @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
- */
- public boolean userSelectNetwork(int netId, boolean persist) {
- WifiConfiguration selected = mWifiConfigManager.getWifiConfiguration(netId);
- localLog("userSelectNetwork:" + netId + " persist:" + persist);
- if (selected == null || selected.SSID == null) {
- localLoge("userSelectNetwork: Bad configuration with nid=" + netId);
- return false;
- }
-
-
- if (!selected.getNetworkSelectionStatus().isNetworkEnabled()) {
- mWifiConfigManager.updateNetworkSelectionStatus(netId,
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
- }
-
- if (!persist) {
- localLog("User has no privilege to overwrite the current priority");
- return false;
- }
-
- boolean change = false;
- String key = selected.configKey();
- // This is only used for setting the connect choice timestamp for debugging purposes.
- long currentTime = mClock.currentTimeMillis();
- List<WifiConfiguration> savedNetworks = mWifiConfigManager.getSavedNetworks();
-
- for (WifiConfiguration network : savedNetworks) {
- WifiConfiguration config = mWifiConfigManager.getWifiConfiguration(network.networkId);
- WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
- if (config.networkId == selected.networkId) {
- if (status.getConnectChoice() != null) {
- localLog("Remove user selection preference of " + status.getConnectChoice()
- + " Set Time: " + status.getConnectChoiceTimestamp() + " from "
- + config.SSID + " : " + config.networkId);
- status.setConnectChoice(null);
- status.setConnectChoiceTimestamp(WifiConfiguration.NetworkSelectionStatus
- .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
- change = true;
- }
- continue;
- }
-
- if (status.getSeenInLastQualifiedNetworkSelection()
- && (status.getConnectChoice() == null
- || !status.getConnectChoice().equals(key))) {
- localLog("Add key:" + key + " Set Time: " + currentTime + " to "
- + getNetworkString(config));
- status.setConnectChoice(key);
- status.setConnectChoiceTimestamp(currentTime);
- change = true;
- }
- }
- //Write this change to file
- if (change) {
- mWifiConfigManager.writeKnownNetworkHistory();
- return true;
- }
-
- return false;
- }
-
- /**
- * 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.
- *
- * @param bssid the bssid to be enabled / disabled
- * @param enable -- true enable a bssid if it has been disabled
- * -- false disable a bssid
- */
- public boolean enableBssidForQualityNetworkSelection(String bssid, boolean enable) {
- if (enable) {
- return (mBssidBlacklist.remove(bssid) != null);
- } else {
- if (bssid != null) {
- BssidBlacklistStatus status = mBssidBlacklist.get(bssid);
- if (status == null) {
- //first time
- BssidBlacklistStatus newStatus = new BssidBlacklistStatus();
- newStatus.mCounter++;
- mBssidBlacklist.put(bssid, newStatus);
- } else if (!status.mIsBlacklisted) {
- status.mCounter++;
- if (status.mCounter >= BSSID_BLACKLIST_THRESHOLD) {
- status.mIsBlacklisted = true;
- status.mBlacklistedTimeStamp = mClock.elapsedRealtime();
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * 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.
- */
- 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) {
- iter.remove();
- }
- }
- }
- }
-
- /**
- * 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);
- return status == null ? false : status.mIsBlacklisted;
- }
-
- private boolean isCarrierNetwork(ScanResult scanResult) {
- return (getMatchingConfigForEAPNetworks(scanResult,
- mCarrierConfiguredNetworks) != null ? true : false);
- }
-
- /**
- * 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
- *
- * @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
- */
- public WifiConfiguration selectQualifiedNetwork(boolean forceSelectNetwork,
- boolean isUntrustedConnectionsAllowed, List<ScanDetail> scanDetails,
- boolean isLinkDebouncing, boolean isConnected, boolean isDisconnected,
- boolean isSupplicantTransient) {
- localLog("==========start qualified Network Selection==========");
- mScanDetails = scanDetails;
- List<Pair<ScanDetail, WifiConfiguration>> filteredScanDetails = new ArrayList<>();
- if (mCurrentConnectedNetwork == null) {
- mCurrentConnectedNetwork =
- mWifiConfigManager.getWifiConfiguration(mWifiInfo.getNetworkId());
- }
-
- // Always get the current BSSID from WifiInfo in case that firmware initiated roaming
- // happened.
- mCurrentBssid = mWifiInfo.getBSSID();
-
- if (!forceSelectNetwork && !needQualifiedNetworkSelection(isLinkDebouncing, isConnected,
- isDisconnected, isSupplicantTransient)) {
- localLog("Quit qualified Network Selection since it is not forced and current network"
- + " is qualified already");
- mFilteredScanDetails = filteredScanDetails;
- return null;
- }
-
- int currentHighestScore = Integer.MIN_VALUE;
- ScanResult scanResultCandidate = null;
- WifiConfiguration networkCandidate = null;
- WifiConfiguration carrierCandidate = null;
- final ExternalScoreEvaluator externalScoreEvaluator =
- new ExternalScoreEvaluator(mLocalLog, mDbg);
- final CarrierScoreEvaluator carrierScoreEvaluator =
- new CarrierScoreEvaluator(mLocalLog, mDbg);
- String lastUserSelectedNetWorkKey = mWifiConfigManager.getLastSelectedConfiguration();
- WifiConfiguration lastUserSelectedNetwork =
- mWifiConfigManager.getWifiConfiguration(lastUserSelectedNetWorkKey);
- if (lastUserSelectedNetwork != null) {
- localLog("Last selection is " + lastUserSelectedNetwork.SSID + " Time to now: "
- + ((mClock.elapsedRealtime() - mWifiConfigManager.getLastSelectedTimeStamp())
- / 1000 / 60 + " minutes"));
- }
-
- updateSavedNetworkSelectionStatus();
- updateBssidBlacklist();
-
- StringBuffer lowSignalScan = new StringBuffer();
- StringBuffer notSavedScan = new StringBuffer();
- StringBuffer noValidSsid = new StringBuffer();
- StringBuffer scoreHistory = new StringBuffer();
- ArrayList<NetworkKey> unscoredNetworks = new ArrayList<NetworkKey>();
- boolean scanResultsHaveCurrentBssid = false;
-
- localLog("isCarrierNetworkEnabledByUser: " +
- mWifiConfigManager.getIsCarrierNetworkEnabledByUser());
-
- //iterate all scan results and find the best candidate with the highest score
- for (ScanDetail scanDetail : mScanDetails) {
- ScanResult scanResult = scanDetail.getScanResult();
- //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 + " / ");
- }
- continue;
- }
-
- //check if the scan results contain the current connected
- //BSSID.
- if (scanResult.BSSID.equals(mCurrentBssid)) {
- scanResultsHaveCurrentBssid = true;
- }
-
- final String scanId = toScanId(scanResult);
- //check whether this BSSID is blocked or not
- if (mWifiConfigManager.isBssidBlacklisted(scanResult.BSSID)
- || isBssidDisabled(scanResult.BSSID)) {
- //We should not see this in ePNO
- Log.e(TAG, scanId + " is in blacklist.");
- continue;
- }
-
- //skip scan result 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 + " / ");
- }
- continue;
- }
-
- //check if there is already a score for this network
- 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
- unscoredNetworks.add(ntwkKey);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "Invalid SSID=" + scanResult.SSID + " BSSID=" + scanResult.BSSID
- + " for network score. Skip.");
- }
- }
-
- //check whether this scan result belong to a saved network
- boolean potentiallyEphemeral = false;
- // Stores WifiConfiguration of potential connection candidates for scan result filtering
- WifiConfiguration potentialEphemeralCandidate = null;
- List<WifiConfiguration> associatedWifiConfigurations =
- mWifiConfigManager.updateSavedNetworkWithNewScanDetail(scanDetail,
- isSupplicantTransient || isConnected || isLinkDebouncing);
- if (associatedWifiConfigurations == null) {
- potentiallyEphemeral = true;
- if (mDbg) {
- notSavedScan.append(scanId + " / ");
- }
- } else if (associatedWifiConfigurations.size() == 1) {
- //if there are more than 1 associated network, it must be a passpoint network
- WifiConfiguration network = associatedWifiConfigurations.get(0);
- if (network.ephemeral) {
- potentialEphemeralCandidate = network;
- potentiallyEphemeral = true;
- }
- }
-
- // Evaluate the potentially ephemeral network as a possible candidate if untrusted
- // connections are allowed and we have an external score for the scan result.
- if (potentiallyEphemeral) {
- if (!mWifiConfigManager.wasEphemeralNetworkDeleted(
- ScanDetailUtil.createQuotedSSID(scanResult.SSID))) {
- if (isUntrustedConnectionsAllowed) {
- Integer netScore = getNetworkScore(scanResult, false);
- if (netScore != null) {
- externalScoreEvaluator.evalUntrustedCandidate(netScore, scanResult);
- // scanDetail is for available ephemeral network
- filteredScanDetails.add(Pair.create(scanDetail,
- potentialEphemeralCandidate));
- }
- // Evaluate the carrier network as a possible candidate.
- // todo need to add flag isCarrierConnectionsAllowed, config in settings.
- } else if (!mCarrierConfiguredNetworks.isEmpty() &&
- isCarrierNetwork(scanResult) &&
- mWifiConfigManager.getIsCarrierNetworkEnabledByUser()) {
- localLog("Checking the carrierScoreEvaluator for candidates...");
- carrierScoreEvaluator.evalCarrierCandidate(scanResult,
- getCarrierScore(scanResult, mCurrentConnectedNetwork,
- (mCurrentBssid == null ? false :
- mCurrentBssid.equals(scanResult.BSSID))));
- filteredScanDetails.add(Pair.create(scanDetail,
- potentialEphemeralCandidate));
- }
- }
- 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.
- int highestScore = Integer.MIN_VALUE;
- int score;
- WifiConfiguration configurationCandidateForThisScan = null;
- WifiConfiguration potentialCandidate = null;
- for (WifiConfiguration network : associatedWifiConfigurations) {
- WifiConfiguration.NetworkSelectionStatus status =
- network.getNetworkSelectionStatus();
- status.setSeenInLastQualifiedNetworkSelection(true);
- if (potentialCandidate == null) {
- potentialCandidate = network;
- }
- if (!status.isNetworkEnabled()) {
- 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:"
- + network.BSSID + ". Skip " + scanResult.BSSID);
- continue;
- }
-
- // If the network is marked to use external scores then attempt to fetch the score.
- // These networks will not be considered alongside the other saved networks.
- if (network.useExternalScores) {
- Integer netScore = getNetworkScore(scanResult, false);
- externalScoreEvaluator.evalSavedCandidate(netScore, network, scanResult);
- continue;
- }
-
- score = calculateBssidScore(scanResult, network, mCurrentConnectedNetwork,
- (mCurrentBssid == null ? false : mCurrentBssid.equals(scanResult.BSSID)),
- (lastUserSelectedNetwork == null ? false : lastUserSelectedNetwork.networkId
- == network.networkId), scoreHistory);
- if (score > highestScore) {
- highestScore = score;
- configurationCandidateForThisScan = network;
- potentialCandidate = network;
- }
- //update the cached candidate
- if (score > status.getCandidateScore() || (score == status.getCandidateScore()
- && status.getCandidate() != null
- && scanResult.level > status.getCandidate().level)) {
- status.setCandidate(scanResult);
- status.setCandidateScore(score);
- }
- }
- // Create potential filteredScanDetail entry
- filteredScanDetails.add(Pair.create(scanDetail, potentialCandidate));
-
- if (highestScore > currentHighestScore || (highestScore == currentHighestScore
- && scanResultCandidate != null
- && scanResult.level > scanResultCandidate.level)) {
- currentHighestScore = highestScore;
- scanResultCandidate = scanResult;
- networkCandidate = configurationCandidateForThisScan;
- networkCandidate.getNetworkSelectionStatus().setCandidate(scanResultCandidate);
- }
- }
-
- mFilteredScanDetails = filteredScanDetails;
-
- //kick the score manager if there is any unscored network
- if (mScoreManager != null && unscoredNetworks.size() != 0) {
- NetworkKey[] unscoredNetworkKeys =
- unscoredNetworks.toArray(new NetworkKey[unscoredNetworks.size()]);
- mScoreManager.requestScores(unscoredNetworkKeys);
- }
-
- 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");
- localLog(scoreHistory.toString());
- }
-
- //QNS listens to all single scan results. Some scan requests may not include
- //the channel of the currently connected network, so the currently connected network
- //won't show up in the scan results. We don't act on these scan results to avoid
- //aggressive network switching which might trigger disconnection.
- if (isConnected && !scanResultsHaveCurrentBssid) {
- localLog("Current connected BSSID " + mCurrentBssid + " is not in the scan results."
- + " Skip network selection.");
- return null;
- }
-
- //we need traverse the whole user preference to choose the one user like most now
- if (scanResultCandidate != null) {
- WifiConfiguration tempConfig = networkCandidate;
-
- while (tempConfig.getNetworkSelectionStatus().getConnectChoice() != null) {
- String key = tempConfig.getNetworkSelectionStatus().getConnectChoice();
- tempConfig = mWifiConfigManager.getWifiConfiguration(key);
-
- if (tempConfig != null) {
- WifiConfiguration.NetworkSelectionStatus tempStatus =
- tempConfig.getNetworkSelectionStatus();
- if (tempStatus.getCandidate() != null && tempStatus.isNetworkEnabled()) {
- scanResultCandidate = tempStatus.getCandidate();
- networkCandidate = tempConfig;
- }
- } else {
- //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:"
- + getNetworkString(networkCandidate) + " : " + scanResultCandidate.BSSID);
- }
-
- // At this point none of the saved networks were good candidates so we fall back to
- // externally scored networks if any are available.
- if (scanResultCandidate == null) {
- localLog("Checking the externalScoreEvaluator for candidates...");
- networkCandidate = getExternalScoreCandidate(externalScoreEvaluator);
- if (networkCandidate != null) {
- scanResultCandidate = networkCandidate.getNetworkSelectionStatus().getCandidate();
- }
- }
-
- if (scanResultCandidate == null) {
- networkCandidate = getCarrierScoreCandidate(carrierScoreEvaluator);
- localLog("Carrier candidate::" + networkCandidate);
- if (networkCandidate != null) {
- scanResultCandidate =
- mWifiConfigManager.getScanResultCandidate(networkCandidate);
- }
- }
-
- if (scanResultCandidate == null) {
- localLog("Can not find any suitable candidates");
- return null;
- }
-
- 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.
- 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();
- return networkCandidate;
- }
-
- /**
- * Returns the best candidate network according to the given ExternalScoreEvaluator.
- */
- @Nullable
- WifiConfiguration getExternalScoreCandidate(ExternalScoreEvaluator scoreEvaluator) {
- WifiConfiguration networkCandidate = null;
- switch (scoreEvaluator.getBestCandidateType()) {
- case ExternalScoreEvaluator.BestCandidateType.UNTRUSTED_NETWORK:
- ScanResult untrustedScanResultCandidate =
- scoreEvaluator.getScanResultCandidate();
- WifiConfiguration unTrustedNetworkCandidate =
- mWifiConfigManager.wifiConfigurationFromScanResult(
- untrustedScanResultCandidate);
-
- // Mark this config as ephemeral so it isn't persisted.
- unTrustedNetworkCandidate.ephemeral = true;
- if (mNetworkScoreCache != null) {
- unTrustedNetworkCandidate.meteredHint =
- mNetworkScoreCache.getMeteredHint(untrustedScanResultCandidate);
- }
- mWifiConfigManager.saveNetwork(unTrustedNetworkCandidate,
- WifiConfiguration.UNKNOWN_UID);
-
- localLog(String.format("new ephemeral candidate %s network ID:%d, "
- + "meteredHint=%b",
- toScanId(untrustedScanResultCandidate), unTrustedNetworkCandidate.networkId,
- unTrustedNetworkCandidate.meteredHint));
-
- unTrustedNetworkCandidate.getNetworkSelectionStatus()
- .setCandidate(untrustedScanResultCandidate);
- networkCandidate = unTrustedNetworkCandidate;
- break;
-
- case ExternalScoreEvaluator.BestCandidateType.SAVED_NETWORK:
- ScanResult scanResultCandidate = scoreEvaluator.getScanResultCandidate();
- networkCandidate = scoreEvaluator.getSavedConfig();
- networkCandidate.getNetworkSelectionStatus().setCandidate(scanResultCandidate);
- localLog(String.format("new scored candidate %s network ID:%d",
- toScanId(scanResultCandidate), networkCandidate.networkId));
- break;
-
- case ExternalScoreEvaluator.BestCandidateType.NONE:
- localLog("ExternalScoreEvaluator did not see any good candidates.");
- break;
-
- default:
- localLoge("Unhandled ExternalScoreEvaluator case. No candidate selected.");
- break;
- }
- return networkCandidate;
- }
-
- /**
- * Returns the best candidate network according to the given CarrierScoreEvaluator.
- */
- @Nullable
- WifiConfiguration getCarrierScoreCandidate(CarrierScoreEvaluator scoreEvaluator) {
-
- ScanResult untrustedCarrierScanResult = scoreEvaluator.getScanResultCandidate();
- if (untrustedCarrierScanResult == null) {
- return null;
- }
-
- WifiConfiguration untrustedCandidateConfig = getMatchingConfigForEAPNetworks(
- untrustedCarrierScanResult, mCarrierConfiguredNetworks);
-
- if (untrustedCandidateConfig == null) {
- return null;
- }
-
- WifiConfiguration newUntrustedCandidateConfig =
- new WifiConfiguration(untrustedCandidateConfig);
-
- // Mark this config as ephemeral so it isn't persisted.
- newUntrustedCandidateConfig.ephemeral = true;
- // Mark this config as a Carrier Network.
- newUntrustedCandidateConfig.isCarrierNetwork = true;
-
- mWifiConfigManager.saveNetworkAndSetCandidate(
- newUntrustedCandidateConfig, untrustedCarrierScanResult);
- return newUntrustedCandidateConfig;
- }
-
- /**
- * Returns the available external network score or NULL if no score is available.
- *
- * @param scanResult The scan result of the network to score.
- * @param isActiveNetwork Whether or not the network is currently connected.
- * @return A valid external score if one is available or NULL.
- */
- @Nullable
- Integer getNetworkScore(ScanResult scanResult, boolean isActiveNetwork) {
- if (mNetworkScoreCache != null && mNetworkScoreCache.isScoredNetwork(scanResult)) {
- int networkScore = mNetworkScoreCache.getNetworkScore(scanResult, isActiveNetwork);
- localLog(toScanId(scanResult) + " has score: " + networkScore);
- return networkScore;
- }
- return null;
- }
-
- /**
- * Returns the available external network score or NULL if no score is available.
- *
- * @param scanResult The scan result of the network to score.
- * @return A valid external score if one is available or NULL.
- */
- int getCarrierScore(ScanResult scanResult, WifiConfiguration currentNetwork,
- boolean sameBssid) {
- localLog("Calc Carrier score: w/" + sameBssid);
- if (currentNetwork != null) {
- localLog("scoring: compare::" + scanResult.SSID + ", with:" + currentNetwork.SSID);
- }
- int score = 0;
- // Calculate the RSSI score.
- int rssi = scanResult.level <= mWifiConfigManager.mThresholdSaturatedRssi24.get()
- ? scanResult.level : mWifiConfigManager.mThresholdSaturatedRssi24.get();
- score += (rssi + mRssiScoreOffset) * mRssiScoreSlope;
-
- // 5GHz band bonus.
- if (scanResult.is5GHz()) {
- score += BAND_AWARD_5GHz;
- }
-
- //same network award
- if ((currentNetwork != null) && currentNetwork.SSID.equals(scanResult.SSID)) {
- score += mWifiConfigManager.mCurrentNetworkBoost.get();
- }
-
- //same BSSID award
- if (sameBssid) {
- score += mSameBssidAward;
- }
-
- localLog("Calc Carrier score:" + score);
- return score;
- }
-
- /**
- * Formats the given ScanResult as a scan ID for logging.
- */
- private static String toScanId(@Nullable ScanResult scanResult) {
- return scanResult == null ? "NULL"
- : String.format("%s:%s", scanResult.SSID, scanResult.BSSID);
- }
-
- //Dump the logs
- void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("Dump of WifiQualifiedNetworkSelector");
- pw.println("WifiQualifiedNetworkSelector - Log Begin ----");
- mLocalLog.dump(fd, pw, args);
- pw.println("WifiQualifiedNetworkSelector - Log End ----");
- }
-
- /**
- * Used to track and evaluate networks that are assigned external scores.
- */
- static class ExternalScoreEvaluator {
- @Retention(RetentionPolicy.SOURCE)
- @interface BestCandidateType {
- int NONE = 0;
- int SAVED_NETWORK = 1;
- int UNTRUSTED_NETWORK = 2;
- }
- // Always set to the best known candidate.
- private @BestCandidateType int mBestCandidateType = BestCandidateType.NONE;
- private int mHighScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE;
- private WifiConfiguration mSavedConfig;
- private ScanResult mScanResultCandidate;
- private final LocalLog mLocalLog;
- private final boolean mDbg;
-
- ExternalScoreEvaluator(LocalLog localLog, boolean dbg) {
- mLocalLog = localLog;
- mDbg = dbg;
- }
-
- // Determines whether or not the given scan result is the best one its seen so far.
- void evalUntrustedCandidate(@Nullable Integer score, ScanResult scanResult) {
- if (score != null && score > mHighScore) {
- mHighScore = score;
- mScanResultCandidate = scanResult;
- mBestCandidateType = BestCandidateType.UNTRUSTED_NETWORK;
- localLog(toScanId(scanResult) + " become the new untrusted candidate");
- }
- }
-
- // Determines whether or not the given saved network is the best one its seen so far.
- void evalSavedCandidate(@Nullable Integer score, WifiConfiguration config,
- ScanResult scanResult) {
- // Always take the highest score. If there's a tie and an untrusted network is currently
- // the best then pick the saved network.
- if (score != null
- && (score > mHighScore
- || (mBestCandidateType == BestCandidateType.UNTRUSTED_NETWORK
- && score == mHighScore))) {
- mHighScore = score;
- mSavedConfig = config;
- mScanResultCandidate = scanResult;
- mBestCandidateType = BestCandidateType.SAVED_NETWORK;
- localLog(toScanId(scanResult) + " become the new externally scored saved network "
- + "candidate");
- }
- }
-
- int getBestCandidateType() {
- return mBestCandidateType;
- }
-
- int getHighScore() {
- return mHighScore;
- }
-
- public ScanResult getScanResultCandidate() {
- return mScanResultCandidate;
- }
-
- WifiConfiguration getSavedConfig() {
- return mSavedConfig;
- }
-
- private void localLog(String log) {
- if (mDbg) {
- mLocalLog.log(log);
- }
- }
- }
-
- /**
- * Used to track and evaluate networks that are assigned by the Carriers.
- */
- static class CarrierScoreEvaluator {
- // Always set to the best known candidate
- private int mHighScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE;
- private ScanResult mScanResultCandidate;
- private final LocalLog mLocalLog;
- private final boolean mDbg;
-
- CarrierScoreEvaluator(LocalLog localLog, boolean dbg) {
- mLocalLog = localLog;
- mDbg = dbg;
- }
-
- // Determines whether or not the given scan result is the best one its seen so far.
- void evalCarrierCandidate(ScanResult scanResult, int score) {
- if (score > mHighScore) {
- mHighScore = score;
- mScanResultCandidate = scanResult;
- localLog(toScanId(scanResult) +
- " become the new untrusted carrier network candidate");
- }
- }
-
- int getHighScore() {
- return mHighScore;
- }
-
- public ScanResult getScanResultCandidate() {
- return mScanResultCandidate;
- }
-
- private void localLog(String log) {
- if (mDbg) {
- mLocalLog.log(log);
- }
- }
- }
-
- private WifiConfiguration getMatchingConfigForEAPNetworks(
- ScanResult scanResult, List<WifiConfiguration> candidateConfigs) {
- if (scanResult == null || candidateConfigs == null) {
- return null;
- }
- // TODO currently we only support EAP. We'll add to this in OC.
- if (!scanResult.capabilities.contains("EAP")) {
- return null;
- }
- String ssid = "\"" + scanResult.SSID + "\"";
- for (WifiConfiguration config : candidateConfigs) {
- if (config.SSID.equals(ssid)) {
- // TODO currently we only support EAP. We'll add to this in OC.
- if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) ||
- config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
- return config;
- }
- }
- }
- return null;
- }
-}
diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java
index d32c722..8bc8bf3 100644
--- a/service/java/com/android/server/wifi/WifiScoreReport.java
+++ b/service/java/com/android/server/wifi/WifiScoreReport.java
@@ -16,342 +16,140 @@
package com.android.server.wifi;
+import android.content.Context;
import android.net.NetworkAgent;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.util.Log;
+import com.android.internal.R;
/**
-* Calculate scores for connected wifi networks.
+ * Class used to calculate scores for connected wifi networks and report it to the associated
+ * network agent.
*/
public class WifiScoreReport {
- // TODO: switch to WifiScoreReport if it doesn't break any tools
- private static final String TAG = "WifiStateMachine";
+ private static final String TAG = "WifiScoreReport";
- // TODO: This score was hardcorded to 56. Need to understand why after finishing code refactor
private static final int STARTING_SCORE = 56;
- // TODO: Understand why these values are used
- private static final int MAX_BAD_LINKSPEED_COUNT = 6;
private static final int SCAN_CACHE_VISIBILITY_MS = 12000;
private static final int HOME_VISIBLE_NETWORK_MAX_COUNT = 6;
private static final int SCAN_CACHE_COUNT_PENALTY = 2;
private static final int AGGRESSIVE_HANDOVER_PENALTY = 6;
- private static final int MIN_SUCCESS_COUNT = 5;
- private static final int MAX_SUCCESS_COUNT_OF_STUCK_LINK = 3;
+ private static final int MAX_SUCCESS_RATE_OF_STUCK_LINK = 3; // proportional to packets per sec
private static final int MAX_STUCK_LINK_COUNT = 5;
- private static final int MIN_NUM_TICKS_AT_STATE = 1000;
- private static final int USER_DISCONNECT_PENALTY = 5;
private static final int MAX_BAD_RSSI_COUNT = 7;
private static final int BAD_RSSI_COUNT_PENALTY = 2;
private static final int MAX_LOW_RSSI_COUNT = 1;
- private static final double MIN_TX_RATE_FOR_WORKING_LINK = 0.3;
+ private static final double MIN_TX_FAILURE_RATE_FOR_WORKING_LINK = 0.3;
private static final int MIN_SUSTAINED_LINK_STUCK_COUNT = 1;
private static final int LINK_STUCK_PENALTY = 2;
private static final int BAD_LINKSPEED_PENALTY = 4;
private static final int GOOD_LINKSPEED_BONUS = 4;
+ // Device configs. The values are examples.
+ private final int mThresholdMinimumRssi5; // -82
+ private final int mThresholdQualifiedRssi5; // -70
+ private final int mThresholdSaturatedRssi5; // -57
+ private final int mThresholdMinimumRssi24; // -85
+ private final int mThresholdQualifiedRssi24; // -73
+ private final int mThresholdSaturatedRssi24; // -60
+ private final int mBadLinkSpeed24; // 6 Mbps
+ private final int mBadLinkSpeed5; // 12 Mbps
+ private final int mGoodLinkSpeed24; // 24 Mbps
+ private final int mGoodLinkSpeed5; // 36 Mbps
+ private final WifiConfigManager mWifiConfigManager;
+ private boolean mVerboseLoggingEnabled = false;
+
+ // Cache of the last score report.
private String mReport;
- private int mBadLinkspeedcount;
+ private boolean mReportValid = false;
- WifiScoreReport(String report, int badLinkspeedcount) {
- mReport = report;
- mBadLinkspeedcount = badLinkspeedcount;
+ // State set by updateScoringState
+ private boolean mMultiBandScanResults;
+ private boolean mIsHomeNetwork;
+
+ WifiScoreReport(Context context, WifiConfigManager wifiConfigManager) {
+ // Fetch all the device configs.
+ mThresholdMinimumRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
+ mThresholdQualifiedRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
+ mThresholdSaturatedRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
+ mThresholdMinimumRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
+ mThresholdQualifiedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
+ mThresholdSaturatedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
+ mBadLinkSpeed24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_link_speed_24);
+ mBadLinkSpeed5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_link_speed_5);
+ mGoodLinkSpeed24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_link_speed_24);
+ mGoodLinkSpeed5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_link_speed_5);
+
+ mWifiConfigManager = wifiConfigManager;
}
/**
- * Method returning the String representation of the score report.
+ * Method returning the String representation of the last score report.
*
* @return String score report
*/
- public String getReport() {
+ public String getLastReport() {
return mReport;
}
/**
- * Method returning the bad link speed count at the time of the current score report.
- *
- * @return int bad linkspeed count
+ * Reset the last calculated score.
*/
- public int getBadLinkspeedcount() {
- return mBadLinkspeedcount;
+ public void reset() {
+ mReport = "";
+ mReportValid = false;
}
/**
- * Calculate wifi network score based on updated link layer stats and return a new
- * WifiScoreReport object.
+ * Checks if the last report data is valid or not. This will be cleared when {@link #reset()} is
+ * invoked.
+ *
+ * @return true if valid, false otherwise.
+ */
+ public boolean isLastReportValid() {
+ return mReportValid;
+ }
+
+ /**
+ * Enable/Disable verbose logging in score report generation.
+ */
+ public void enableVerboseLogging(boolean enable) {
+ mVerboseLoggingEnabled = enable;
+ }
+
+ /**
+ * Calculate wifi network score based on updated link layer stats and send the score to
+ * the provided network agent.
*
* If the score has changed from the previous value, update the WifiNetworkAgent.
- * @param wifiInfo WifiInfo information about current network connection
- * @param currentConfiguration WifiConfiguration current wifi config
- * @param wifiConfigManager WifiConfigManager Object holding current config state
- * @param networkAgent NetworkAgent to be notified of new score
- * @param lastReport String most recent score report
- * @param aggressiveHandover int current aggressiveHandover setting
- * @return WifiScoreReport Wifi Score report
+ *
+ * Called periodically (POLL_RSSI_INTERVAL_MSECS) about every 3 seconds.
+ *
+ * @param wifiInfo WifiInfo instance pointing to the currently connected network.
+ * @param networkAgent NetworkAgent to be notified of new score.
+ * @param aggressiveHandover int current aggressiveHandover setting.
+ * @param wifiMetrics for reporting our scores.
*/
- public static WifiScoreReport calculateScore(WifiInfo wifiInfo,
- WifiConfiguration currentConfiguration,
- WifiConfigManager wifiConfigManager,
- NetworkAgent networkAgent,
- WifiScoreReport lastReport,
- int aggressiveHandover,
- WifiMetrics wifiMetrics) {
- boolean debugLogging = false;
- if (wifiConfigManager.mEnableVerboseLogging.get() > 0) {
- debugLogging = true;
- }
+ public void calculateAndReportScore(WifiInfo wifiInfo, NetworkAgent networkAgent,
+ int aggressiveHandover, WifiMetrics wifiMetrics) {
+ int score;
- StringBuilder sb = new StringBuilder();
-
- int score = STARTING_SCORE;
- boolean isBadLinkspeed = (wifiInfo.is24GHz()
- && wifiInfo.getLinkSpeed() < wifiConfigManager.mBadLinkSpeed24)
- || (wifiInfo.is5GHz() && wifiInfo.getLinkSpeed()
- < wifiConfigManager.mBadLinkSpeed5);
- boolean isGoodLinkspeed = (wifiInfo.is24GHz()
- && wifiInfo.getLinkSpeed() >= wifiConfigManager.mGoodLinkSpeed24)
- || (wifiInfo.is5GHz() && wifiInfo.getLinkSpeed()
- >= wifiConfigManager.mGoodLinkSpeed5);
-
- int badLinkspeedcount = 0;
- if (lastReport != null) {
- badLinkspeedcount = lastReport.getBadLinkspeedcount();
- }
-
- if (isBadLinkspeed) {
- if (badLinkspeedcount < MAX_BAD_LINKSPEED_COUNT) {
- badLinkspeedcount++;
- }
- } else {
- if (badLinkspeedcount > 0) {
- badLinkspeedcount--;
- }
- }
-
- if (isBadLinkspeed) sb.append(" bl(").append(badLinkspeedcount).append(")");
- if (isGoodLinkspeed) sb.append(" gl");
-
- /**
- * We want to make sure that we use the 24GHz RSSI thresholds if
- * there are 2.4GHz scan results
- * otherwise we end up lowering the score based on 5GHz values
- * which may cause a switch to LTE before roaming has a chance to try 2.4GHz
- * We also might unblacklist the configuation based on 2.4GHz
- * thresholds but joining 5GHz anyhow, and failing over to 2.4GHz because 5GHz is not good
- */
- boolean use24Thresholds = false;
- boolean homeNetworkBoost = false;
- ScanDetailCache scanDetailCache =
- wifiConfigManager.getScanDetailCache(currentConfiguration);
- if (currentConfiguration != null && scanDetailCache != null) {
- currentConfiguration.setVisibility(
- scanDetailCache.getVisibility(SCAN_CACHE_VISIBILITY_MS));
- if (currentConfiguration.visibility != null) {
- if (currentConfiguration.visibility.rssi24 != WifiConfiguration.INVALID_RSSI
- && currentConfiguration.visibility.rssi24
- >= (currentConfiguration.visibility.rssi5 - SCAN_CACHE_COUNT_PENALTY)) {
- use24Thresholds = true;
- }
- }
- if (scanDetailCache.size() <= HOME_VISIBLE_NETWORK_MAX_COUNT
- && currentConfiguration.allowedKeyManagement.cardinality() == 1
- && currentConfiguration.allowedKeyManagement
- .get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
- // A PSK network with less than 6 known BSSIDs
- // This is most likely a home network and thus we want to stick to wifi more
- homeNetworkBoost = true;
- }
- }
- if (homeNetworkBoost) sb.append(" hn");
- if (use24Thresholds) sb.append(" u24");
-
- int rssi = wifiInfo.getRssi() - AGGRESSIVE_HANDOVER_PENALTY * aggressiveHandover
- + (homeNetworkBoost ? WifiConfiguration.HOME_NETWORK_RSSI_BOOST : 0);
- sb.append(String.format(" rssi=%d ag=%d", rssi, aggressiveHandover));
-
- boolean is24GHz = use24Thresholds || wifiInfo.is24GHz();
-
- boolean isBadRSSI = (is24GHz && rssi < wifiConfigManager.mThresholdMinimumRssi24.get())
- || (!is24GHz && rssi < wifiConfigManager.mThresholdMinimumRssi5.get());
- boolean isLowRSSI = (is24GHz && rssi < wifiConfigManager.mThresholdQualifiedRssi24.get())
- || (!is24GHz
- && wifiInfo.getRssi() < wifiConfigManager.mThresholdMinimumRssi5.get());
- boolean isHighRSSI = (is24GHz && rssi >= wifiConfigManager.mThresholdSaturatedRssi24.get())
- || (!is24GHz
- && wifiInfo.getRssi() >= wifiConfigManager.mThresholdSaturatedRssi5.get());
-
- if (isBadRSSI) sb.append(" br");
- if (isLowRSSI) sb.append(" lr");
- if (isHighRSSI) sb.append(" hr");
-
- int penalizedDueToUserTriggeredDisconnect = 0; // Not a user triggered disconnect
- if (currentConfiguration != null
- && (wifiInfo.txSuccessRate > MIN_SUCCESS_COUNT
- || wifiInfo.rxSuccessRate > MIN_SUCCESS_COUNT)) {
- if (isBadRSSI) {
- currentConfiguration.numTicksAtBadRSSI++;
- if (currentConfiguration.numTicksAtBadRSSI > MIN_NUM_TICKS_AT_STATE) {
- // We remained associated for a compound amount of time while passing
- // traffic, hence loose the corresponding user triggered disabled stats
- if (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0) {
- currentConfiguration.numUserTriggeredWifiDisableBadRSSI--;
- }
- if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
- currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
- }
- if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
- currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
- }
- currentConfiguration.numTicksAtBadRSSI = 0;
- }
- if (wifiConfigManager.mEnableWifiCellularHandoverUserTriggeredAdjustment
- && (currentConfiguration.numUserTriggeredWifiDisableBadRSSI > 0
- || currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
- || currentConfiguration
- .numUserTriggeredWifiDisableNotHighRSSI > 0)) {
- score = score - USER_DISCONNECT_PENALTY;
- penalizedDueToUserTriggeredDisconnect = 1;
- sb.append(" p1");
- }
- } else if (isLowRSSI) {
- currentConfiguration.numTicksAtLowRSSI++;
- if (currentConfiguration.numTicksAtLowRSSI > MIN_NUM_TICKS_AT_STATE) {
- // We remained associated for a compound amount of time while passing
- // traffic, hence loose the corresponding user triggered disabled stats
- if (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0) {
- currentConfiguration.numUserTriggeredWifiDisableLowRSSI--;
- }
- if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
- currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
- }
- currentConfiguration.numTicksAtLowRSSI = 0;
- }
- if (wifiConfigManager.mEnableWifiCellularHandoverUserTriggeredAdjustment
- && (currentConfiguration.numUserTriggeredWifiDisableLowRSSI > 0
- || currentConfiguration
- .numUserTriggeredWifiDisableNotHighRSSI > 0)) {
- score = score - USER_DISCONNECT_PENALTY;
- penalizedDueToUserTriggeredDisconnect = 2;
- sb.append(" p2");
- }
- } else if (!isHighRSSI) {
- currentConfiguration.numTicksAtNotHighRSSI++;
- if (currentConfiguration.numTicksAtNotHighRSSI > MIN_NUM_TICKS_AT_STATE) {
- // We remained associated for a compound amount of time while passing
- // traffic, hence loose the corresponding user triggered disabled stats
- if (currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
- currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI--;
- }
- currentConfiguration.numTicksAtNotHighRSSI = 0;
- }
- if (wifiConfigManager.mEnableWifiCellularHandoverUserTriggeredAdjustment
- && currentConfiguration.numUserTriggeredWifiDisableNotHighRSSI > 0) {
- score = score - USER_DISCONNECT_PENALTY;
- penalizedDueToUserTriggeredDisconnect = 3;
- sb.append(" p3");
- }
- }
- sb.append(String.format(" ticks %d,%d,%d", currentConfiguration.numTicksAtBadRSSI,
- currentConfiguration.numTicksAtLowRSSI,
- currentConfiguration.numTicksAtNotHighRSSI));
- }
-
- if (debugLogging) {
- String rssiStatus = "";
- if (isBadRSSI) {
- rssiStatus += " badRSSI ";
- } else if (isHighRSSI) {
- rssiStatus += " highRSSI ";
- } else if (isLowRSSI) {
- rssiStatus += " lowRSSI ";
- }
- if (isBadLinkspeed) rssiStatus += " lowSpeed ";
- Log.d(TAG, "calculateWifiScore freq=" + Integer.toString(wifiInfo.getFrequency())
- + " speed=" + Integer.toString(wifiInfo.getLinkSpeed())
- + " score=" + Integer.toString(wifiInfo.score)
- + rssiStatus
- + " -> txbadrate=" + String.format("%.2f", wifiInfo.txBadRate)
- + " txgoodrate=" + String.format("%.2f", wifiInfo.txSuccessRate)
- + " txretriesrate=" + String.format("%.2f", wifiInfo.txRetriesRate)
- + " rxrate=" + String.format("%.2f", wifiInfo.rxSuccessRate)
- + " userTriggerdPenalty" + penalizedDueToUserTriggeredDisconnect);
- }
-
- if ((wifiInfo.txBadRate >= 1) && (wifiInfo.txSuccessRate < MAX_SUCCESS_COUNT_OF_STUCK_LINK)
- && (isBadRSSI || isLowRSSI)) {
- // Link is stuck
- if (wifiInfo.linkStuckCount < MAX_STUCK_LINK_COUNT) {
- wifiInfo.linkStuckCount += 1;
- }
- sb.append(String.format(" ls+=%d", wifiInfo.linkStuckCount));
- if (debugLogging) {
- Log.d(TAG, " bad link -> stuck count ="
- + Integer.toString(wifiInfo.linkStuckCount));
- }
- } else if (wifiInfo.txBadRate < MIN_TX_RATE_FOR_WORKING_LINK) {
- if (wifiInfo.linkStuckCount > 0) {
- wifiInfo.linkStuckCount -= 1;
- }
- sb.append(String.format(" ls-=%d", wifiInfo.linkStuckCount));
- if (debugLogging) {
- Log.d(TAG, " good link -> stuck count ="
- + Integer.toString(wifiInfo.linkStuckCount));
- }
- }
-
- sb.append(String.format(" [%d", score));
-
- if (wifiInfo.linkStuckCount > MIN_SUSTAINED_LINK_STUCK_COUNT) {
- // Once link gets stuck for more than 3 seconds, start reducing the score
- score = score - LINK_STUCK_PENALTY * (wifiInfo.linkStuckCount - 1);
- }
- sb.append(String.format(",%d", score));
-
- if (isBadLinkspeed) {
- score -= BAD_LINKSPEED_PENALTY;
- if (debugLogging) {
- Log.d(TAG, " isBadLinkspeed ---> count=" + badLinkspeedcount
- + " score=" + Integer.toString(score));
- }
- } else if ((isGoodLinkspeed) && (wifiInfo.txSuccessRate > 5)) {
- score += GOOD_LINKSPEED_BONUS; // So as bad rssi alone dont kill us
- }
- sb.append(String.format(",%d", score));
-
- if (isBadRSSI) {
- if (wifiInfo.badRssiCount < MAX_BAD_RSSI_COUNT) {
- wifiInfo.badRssiCount += 1;
- }
- } else if (isLowRSSI) {
- wifiInfo.lowRssiCount = MAX_LOW_RSSI_COUNT; // Dont increment the lowRssi count above 1
- if (wifiInfo.badRssiCount > 0) {
- // Decrement bad Rssi count
- wifiInfo.badRssiCount -= 1;
- }
- } else {
- wifiInfo.badRssiCount = 0;
- wifiInfo.lowRssiCount = 0;
- }
-
- score -= wifiInfo.badRssiCount * BAD_RSSI_COUNT_PENALTY + wifiInfo.lowRssiCount;
- sb.append(String.format(",%d", score));
-
- if (debugLogging) {
- Log.d(TAG, " badRSSI count" + Integer.toString(wifiInfo.badRssiCount)
- + " lowRSSI count" + Integer.toString(wifiInfo.lowRssiCount)
- + " --> score " + Integer.toString(score));
- }
-
- if (isHighRSSI) {
- score += 5;
- if (debugLogging) Log.d(TAG, " isHighRSSI ---> score=" + Integer.toString(score));
- }
- sb.append(String.format(",%d]", score));
-
- sb.append(String.format(" brc=%d lrc=%d", wifiInfo.badRssiCount, wifiInfo.lowRssiCount));
+ updateScoringState(wifiInfo, aggressiveHandover);
+ score = calculateScore(wifiInfo, aggressiveHandover);
//sanitize boundaries
if (score > NetworkAgent.WIFI_BASE_SCORE) {
@@ -363,15 +161,163 @@
//report score
if (score != wifiInfo.score) {
- if (debugLogging) {
- Log.d(TAG, "calculateWifiScore() report new score " + Integer.toString(score));
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, " report new wifi score " + score);
}
wifiInfo.score = score;
if (networkAgent != null) {
networkAgent.sendNetworkScore(score);
}
}
+
+ mReport = String.format(" score=%d", score);
+ mReportValid = true;
wifiMetrics.incrementWifiScoreCount(score);
- return new WifiScoreReport(sb.toString(), badLinkspeedcount);
+ }
+
+ /**
+ * Updates the state.
+ */
+ private void updateScoringState(WifiInfo wifiInfo, int aggressiveHandover) {
+ mMultiBandScanResults = multiBandScanResults(wifiInfo);
+ mIsHomeNetwork = isHomeNetwork(wifiInfo);
+
+ int rssiThreshBad = mThresholdMinimumRssi24;
+ int rssiThreshLow = mThresholdQualifiedRssi24;
+
+ if (wifiInfo.is5GHz() && !mMultiBandScanResults) {
+ rssiThreshBad = mThresholdMinimumRssi5;
+ rssiThreshLow = mThresholdQualifiedRssi5;
+ }
+
+ int rssi = wifiInfo.getRssi();
+ if (aggressiveHandover != 0) {
+ rssi -= AGGRESSIVE_HANDOVER_PENALTY * aggressiveHandover;
+ }
+ if (mIsHomeNetwork) {
+ rssi += WifiConfiguration.HOME_NETWORK_RSSI_BOOST;
+ }
+
+ if ((wifiInfo.txBadRate >= 1)
+ && (wifiInfo.txSuccessRate < MAX_SUCCESS_RATE_OF_STUCK_LINK)
+ && rssi < rssiThreshLow) {
+ // Link is stuck
+ if (wifiInfo.linkStuckCount < MAX_STUCK_LINK_COUNT) {
+ wifiInfo.linkStuckCount += 1;
+ }
+ } else if (wifiInfo.txBadRate < MIN_TX_FAILURE_RATE_FOR_WORKING_LINK) {
+ if (wifiInfo.linkStuckCount > 0) {
+ wifiInfo.linkStuckCount -= 1;
+ }
+ }
+
+ if (rssi < rssiThreshBad) {
+ if (wifiInfo.badRssiCount < MAX_BAD_RSSI_COUNT) {
+ wifiInfo.badRssiCount += 1;
+ }
+ } else if (rssi < rssiThreshLow) {
+ wifiInfo.lowRssiCount = MAX_LOW_RSSI_COUNT; // Dont increment the lowRssi count above 1
+ if (wifiInfo.badRssiCount > 0) {
+ // Decrement bad Rssi count
+ wifiInfo.badRssiCount -= 1;
+ }
+ } else {
+ wifiInfo.badRssiCount = 0;
+ wifiInfo.lowRssiCount = 0;
+ }
+
+ }
+
+ /**
+ * Calculates the score, without all the cruft.
+ */
+ private int calculateScore(WifiInfo wifiInfo, int aggressiveHandover) {
+ int score = STARTING_SCORE;
+
+ int rssiThreshSaturated = mThresholdSaturatedRssi24;
+ int linkspeedThreshBad = mBadLinkSpeed24;
+ int linkspeedThreshGood = mGoodLinkSpeed24;
+
+ if (wifiInfo.is5GHz()) {
+ if (!mMultiBandScanResults) {
+ rssiThreshSaturated = mThresholdSaturatedRssi5;
+ }
+ linkspeedThreshBad = mBadLinkSpeed5;
+ linkspeedThreshGood = mGoodLinkSpeed5;
+ }
+
+ int rssi = wifiInfo.getRssi();
+ if (aggressiveHandover != 0) {
+ rssi -= AGGRESSIVE_HANDOVER_PENALTY * aggressiveHandover;
+ }
+ if (mIsHomeNetwork) {
+ rssi += WifiConfiguration.HOME_NETWORK_RSSI_BOOST;
+ }
+
+ int linkSpeed = wifiInfo.getLinkSpeed();
+
+ if (wifiInfo.linkStuckCount > MIN_SUSTAINED_LINK_STUCK_COUNT) {
+ // Once link gets stuck for more than 3 seconds, start reducing the score
+ score = score - LINK_STUCK_PENALTY * (wifiInfo.linkStuckCount - 1);
+ }
+
+ if (linkSpeed < linkspeedThreshBad) {
+ score -= BAD_LINKSPEED_PENALTY;
+ } else if ((linkSpeed >= linkspeedThreshGood) && (wifiInfo.txSuccessRate > 5)) {
+ score += GOOD_LINKSPEED_BONUS; // So as bad rssi alone doesn't kill us
+ }
+
+ score -= wifiInfo.badRssiCount * BAD_RSSI_COUNT_PENALTY + wifiInfo.lowRssiCount;
+
+ if (rssi >= rssiThreshSaturated) score += 5;
+
+ if (score > NetworkAgent.WIFI_BASE_SCORE) score = NetworkAgent.WIFI_BASE_SCORE;
+ if (score < 0) score = 0;
+
+ return score;
+ }
+
+ /**
+ * Determines if we can see both 2.4GHz and 5GHz for current config
+ */
+ private boolean multiBandScanResults(WifiInfo wifiInfo) {
+ WifiConfiguration currentConfiguration =
+ mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
+ if (currentConfiguration == null) return false;
+ ScanDetailCache scanDetailCache =
+ mWifiConfigManager.getScanDetailCacheForNetwork(wifiInfo.getNetworkId());
+ if (scanDetailCache == null) return false;
+ // Nasty that we change state here...
+ currentConfiguration.setVisibility(scanDetailCache.getVisibility(SCAN_CACHE_VISIBILITY_MS));
+ if (currentConfiguration.visibility == null) return false;
+ if (currentConfiguration.visibility.rssi24 == WifiConfiguration.INVALID_RSSI) return false;
+ if (currentConfiguration.visibility.rssi5 == WifiConfiguration.INVALID_RSSI) return false;
+ // N.B. this does not do exactly what is claimed!
+ if (currentConfiguration.visibility.rssi24
+ >= currentConfiguration.visibility.rssi5 - SCAN_CACHE_COUNT_PENALTY) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Decides whether the current network is a "home" network
+ */
+ private boolean isHomeNetwork(WifiInfo wifiInfo) {
+ WifiConfiguration currentConfiguration =
+ mWifiConfigManager.getConfiguredNetwork(wifiInfo.getNetworkId());
+ if (currentConfiguration == null) return false;
+ // This seems like it will only return true for really old routers!
+ if (currentConfiguration.allowedKeyManagement.cardinality() != 1) return false;
+ if (!currentConfiguration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ return false;
+ }
+ ScanDetailCache scanDetailCache =
+ mWifiConfigManager.getScanDetailCacheForNetwork(wifiInfo.getNetworkId());
+ if (scanDetailCache == null) return false;
+ if (scanDetailCache.size() <= HOME_VISIBLE_NETWORK_MAX_COUNT) {
+ return true;
+ }
+ return false;
}
}
diff --git a/service/java/com/android/server/wifi/WifiService.java b/service/java/com/android/server/wifi/WifiService.java
index 28e5b18..d298fbb 100644
--- a/service/java/com/android/server/wifi/WifiService.java
+++ b/service/java/com/android/server/wifi/WifiService.java
@@ -20,6 +20,7 @@
import android.util.Log;
import com.android.server.SystemService;
+import com.android.server.wifi.util.WifiAsyncChannel;
public final class WifiService extends SystemService {
@@ -28,7 +29,7 @@
public WifiService(Context context) {
super(context);
- mImpl = new WifiServiceImpl(context);
+ mImpl = new WifiServiceImpl(context, new WifiInjector(context), new WifiAsyncChannel(TAG));
}
@Override
@@ -48,4 +49,14 @@
public void onSwitchUser(int userId) {
mImpl.handleUserSwitch(userId);
}
+
+ @Override
+ public void onUnlockUser(int userId) {
+ mImpl.handleUserUnlock(userId);
+ }
+
+ @Override
+ public void onStopUser(int userId) {
+ mImpl.handleUserStop(userId);
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index ae42db0..5c9548a 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -16,6 +16,19 @@
package com.android.server.wifi;
+import static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_FAILURE_REASON;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
+import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+
+import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR;
import static com.android.server.wifi.WifiController.CMD_AIRPLANE_TOGGLED;
import static com.android.server.wifi.WifiController.CMD_BATTERY_CHANGED;
import static com.android.server.wifi.WifiController.CMD_EMERGENCY_CALL_STATE_CHANGED;
@@ -30,6 +43,7 @@
import android.Manifest;
import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.AppOpsManager;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
@@ -38,33 +52,33 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
+import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
-import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.DhcpResults;
+import android.net.IpConfiguration;
import android.net.Network;
-import android.net.NetworkScorerAppManager;
import android.net.NetworkUtils;
+import android.net.StaticIpConfiguration;
import android.net.Uri;
import android.net.ip.IpManager;
import android.net.wifi.IWifiManager;
-import android.net.wifi.PasspointManagementObjectDefinition;
import android.net.wifi.ScanResult;
import android.net.wifi.ScanSettings;
import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConnectionStatistics;
-import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiLinkLayerStats;
import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
+import android.net.wifi.WifiScanner;
+import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.AsyncTask;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
@@ -74,26 +88,24 @@
import android.os.Process;
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;
import android.provider.Settings;
-import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
-import com.android.internal.R;
-import com.android.internal.app.IBatteryStats;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
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;
+import com.android.server.wifi.hotspot2.PasspointProvider;
+import com.android.server.wifi.util.WifiHandler;
+import com.android.server.wifi.util.WifiPermissionsUtil;
import java.io.BufferedReader;
import java.io.FileDescriptor;
@@ -107,13 +119,14 @@
import java.security.KeyStore;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
-import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
/**
* WifiService handles remote WiFi operation requests by implementing
@@ -125,23 +138,32 @@
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";
+
+ // Dumpsys argument to enable/disable disconnect on IP reachability failures.
+ private static final String DUMP_ARG_SET_IPREACH_DISCONNECT = "set-ipreach-disconnect";
+ private static final String DUMP_ARG_SET_IPREACH_DISCONNECT_ENABLED = "enabled";
+ private static final String DUMP_ARG_SET_IPREACH_DISCONNECT_DISABLED = "disabled";
+
+ // Default scan background throttling interval if not overriden in settings
+ private static final long DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000;
+
+ // Apps with importance higher than this value is considered as background app.
+ private static final int BACKGROUND_IMPORTANCE_CUTOFF =
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
final WifiStateMachine mWifiStateMachine;
private final Context mContext;
private final FrameworkFacade mFacade;
+ private final Clock mClock;
+ private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
- 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;
+ private final ActivityManager mActivityManager;
private final WifiCountryCode mCountryCode;
+ private long mBackgroundThrottleInterval;
// Debug counter tracking scan requests sent by WifiManager
private int scanRequestCounter = 0;
@@ -157,24 +179,60 @@
private final WifiCertManager mCertManager;
private final WifiInjector mWifiInjector;
+ /* Backup/Restore Module */
+ private final WifiBackupRestore mWifiBackupRestore;
+
+ // Map of package name of background scan apps and last scan timestamp.
+ private final ArrayMap<String, Long> mLastScanTimestamps;
+
+ private WifiScanner mWifiScanner;
+ private WifiLog mLog;
+
/**
* Asynchronous channel to WifiStateMachine
*/
private AsyncChannel mWifiStateMachineChannel;
private final boolean mPermissionReviewRequired;
+ private final FrameworkFacade mFrameworkFacade;
+
+ private WifiPermissionsUtil mWifiPermissionsUtil;
+
+ @GuardedBy("mLocalOnlyHotspotRequests")
+ private final HashMap<Integer, LocalOnlyHotspotRequestInfo> mLocalOnlyHotspotRequests;
+ @GuardedBy("mLocalOnlyHotspotRequests")
+ private WifiConfiguration mLocalOnlyHotspotConfig = null;
+ @GuardedBy("mLocalOnlyHotspotRequests")
+ private final ConcurrentHashMap<String, Integer> mIfaceIpModes;
+
+ /**
+ * Callback for use with LocalOnlyHotspot to unregister requesting applications upon death.
+ *
+ * @hide
+ */
+ public final class LocalOnlyRequestorCallback
+ implements LocalOnlyHotspotRequestInfo.RequestingApplicationDeathCallback {
+ /**
+ * Called with requesting app has died.
+ */
+ @Override
+ public void onLocalOnlyHotspotRequestorDeath(LocalOnlyHotspotRequestInfo requestor) {
+ unregisterCallingAppAndStopLocalOnlyHotspot(requestor);
+ };
+ }
/**
* Handles client connections
*/
- private class ClientHandler extends Handler {
+ private class ClientHandler extends WifiHandler {
- ClientHandler(Looper looper) {
- super(looper);
+ ClientHandler(String tag, Looper looper) {
+ super(tag, looper);
}
@Override
public void handleMessage(Message msg) {
+ super.handleMessage(msg);
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
@@ -197,46 +255,49 @@
break;
}
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
- AsyncChannel ac = new AsyncChannel();
+ AsyncChannel ac = mFrameworkFacade.makeWifiAsyncChannel(TAG);
ac.connect(mContext, this, msg.replyTo);
break;
}
- /* Client commands are forwarded to state machine */
- case WifiManager.CONNECT_NETWORK:
- case WifiManager.SAVE_NETWORK: {
+ case WifiManager.CONNECT_NETWORK: {
WifiConfiguration config = (WifiConfiguration) msg.obj;
int networkId = msg.arg1;
- if (msg.what == WifiManager.SAVE_NETWORK) {
- Slog.d("WiFiServiceImpl ", "SAVE"
- + " nid=" + Integer.toString(networkId)
- + " uid=" + msg.sendingUid
- + " name="
- + mContext.getPackageManager().getNameForUid(msg.sendingUid));
- }
- if (msg.what == WifiManager.CONNECT_NETWORK) {
- Slog.d("WiFiServiceImpl ", "CONNECT "
- + " nid=" + Integer.toString(networkId)
- + " uid=" + msg.sendingUid
- + " name="
- + mContext.getPackageManager().getNameForUid(msg.sendingUid));
- }
-
+ Slog.d("WiFiServiceImpl ", "CONNECT "
+ + " nid=" + Integer.toString(networkId)
+ + " uid=" + msg.sendingUid
+ + " name="
+ + mContext.getPackageManager().getNameForUid(msg.sendingUid));
if (config != null && isValid(config)) {
- if (DBG) Slog.d(TAG, "Connect with config" + config);
+ if (DBG) Slog.d(TAG, "Connect with config " + config);
+ /* Command is forwarded to state machine */
mWifiStateMachine.sendMessage(Message.obtain(msg));
} else if (config == null
&& networkId != WifiConfiguration.INVALID_NETWORK_ID) {
- if (DBG) Slog.d(TAG, "Connect with networkId" + networkId);
+ if (DBG) Slog.d(TAG, "Connect with networkId " + networkId);
mWifiStateMachine.sendMessage(Message.obtain(msg));
} else {
Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
- if (msg.what == WifiManager.CONNECT_NETWORK) {
- replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
- WifiManager.INVALID_ARGS);
- } else {
- replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
- WifiManager.INVALID_ARGS);
- }
+ replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.INVALID_ARGS);
+ }
+ break;
+ }
+ case WifiManager.SAVE_NETWORK: {
+ WifiConfiguration config = (WifiConfiguration) msg.obj;
+ int networkId = msg.arg1;
+ Slog.d("WiFiServiceImpl ", "SAVE"
+ + " nid=" + Integer.toString(networkId)
+ + " uid=" + msg.sendingUid
+ + " name="
+ + mContext.getPackageManager().getNameForUid(msg.sendingUid));
+ if (config != null && isValid(config)) {
+ if (DBG) Slog.d(TAG, "Save network with config " + config);
+ /* Command is forwarded to state machine */
+ mWifiStateMachine.sendMessage(Message.obtain(msg));
+ } else {
+ Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
+ replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
+ WifiManager.INVALID_ARGS);
}
break;
}
@@ -258,7 +319,8 @@
}
private void replyFailed(Message msg, int what, int why) {
- Message reply = msg.obtain();
+ if (msg.replyTo == null) return;
+ Message reply = Message.obtain();
reply.what = what;
reply.arg1 = why;
try {
@@ -273,17 +335,18 @@
/**
* Handles interaction with WifiStateMachine
*/
- private class WifiStateMachineHandler extends Handler {
+ private class WifiStateMachineHandler extends WifiHandler {
private AsyncChannel mWsmChannel;
- WifiStateMachineHandler(Looper looper) {
- super(looper);
- mWsmChannel = new AsyncChannel();
+ WifiStateMachineHandler(String tag, Looper looper, AsyncChannel asyncChannel) {
+ super(tag, looper);
+ mWsmChannel = asyncChannel;
mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
}
@Override
public void handleMessage(Message msg) {
+ super.handleMessage(msg);
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
@@ -310,68 +373,81 @@
}
WifiStateMachineHandler mWifiStateMachineHandler;
-
private WifiController mWifiController;
private final WifiLockManager mWifiLockManager;
+ private final WifiMulticastLockManager mWifiMulticastLockManager;
- public WifiServiceImpl(Context context) {
+ public WifiServiceImpl(Context context, WifiInjector wifiInjector, AsyncChannel asyncChannel) {
mContext = context;
- mWifiInjector = WifiInjector.getInstance();
- mFacade = new FrameworkFacade();
- HandlerThread wifiThread = new HandlerThread("WifiService");
- wifiThread.start();
+ mWifiInjector = wifiInjector;
+ mClock = wifiInjector.getClock();
+
+ mFacade = mWifiInjector.getFrameworkFacade();
mWifiMetrics = mWifiInjector.getWifiMetrics();
- mTrafficPoller = new WifiTrafficPoller(mContext, wifiThread.getLooper(),
- WifiNative.getWlanNativeInterface().getInterfaceName());
- 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);
+ mTrafficPoller = mWifiInjector.getWifiTrafficPoller();
+ mUserManager = mWifiInjector.getUserManager();
+ 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);
-
- mNotificationController = new WifiNotificationController(mContext,
- wifiThread.getLooper(), mWifiStateMachine, mFacade, null);
-
- 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);
-
+ mSettingsStore = mWifiInjector.getWifiSettingsStore();
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ mCertManager = mWifiInjector.getWifiCertManager();
+ mNotificationController = mWifiInjector.getWifiNotificationController();
+ mWifiLockManager = mWifiInjector.getWifiLockManager();
+ mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager();
+ HandlerThread wifiServiceHandlerThread = mWifiInjector.getWifiServiceHandlerThread();
+ mClientHandler = new ClientHandler(TAG, wifiServiceHandlerThread.getLooper());
+ mWifiStateMachineHandler = new WifiStateMachineHandler(TAG,
+ wifiServiceHandlerThread.getLooper(), asyncChannel);
+ mWifiController = mWifiInjector.getWifiController();
+ mWifiBackupRestore = mWifiInjector.getWifiBackupRestore();
mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED
|| context.getResources().getBoolean(
com.android.internal.R.bool.config_permissionReviewRequired);
+ mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil();
+ mLog = mWifiInjector.makeLog(TAG);
+ mFrameworkFacade = wifiInjector.getFrameworkFacade();
+ mLastScanTimestamps = new ArrayMap<>();
+ updateBackgroundThrottleInterval();
+ updateBackgroundThrottlingWhitelist();
+ mIfaceIpModes = new ConcurrentHashMap<>();
+ mLocalOnlyHotspotRequests = new HashMap<>();
+ enableVerboseLoggingInternal(getVerboseLoggingLevel());
}
+ /**
+ * Provide a way for unit tests to set valid log object in the WifiHandler
+ * @param log WifiLog object to assign to the clientHandler
+ */
+ @VisibleForTesting
+ public void setWifiHandlerLogForTest(WifiLog log) {
+ mClientHandler.setWifiLog(log);
+ }
/**
- * Check if Wi-Fi needs to be enabled and start
- * if needed
+ * Check if we are ready to start wifi.
*
- * This function is used only at boot time
+ * First check if we will be restarting system services to decrypt the device. If the device is
+ * not encrypted, check if Wi-Fi needs to be enabled and start if needed
+ *
+ * This function is used only at boot time.
*/
public void checkAndStartWifi() {
- /* Check if wi-fi needs to be enabled */
+ // First check if we will end up restarting WifiService
+ if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) {
+ Log.d(TAG, "Device still encrypted. Need to restart SystemServer. Do not start wifi.");
+ return;
+ }
+
+ // Check if wi-fi needs to be enabled
boolean wifiEnabled = mSettingsStore.isWifiToggleEnabled();
Slog.i(TAG, "WifiService starting up with Wi-Fi " +
(wifiEnabled ? "enabled" : "disabled"));
registerForScanModeChange();
+ registerForBackgroundThrottleChanges();
mContext.registerReceiver(
new BroadcastReceiver() {
@Override
@@ -405,6 +481,25 @@
},
new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED));
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int currState = intent.getIntExtra(EXTRA_WIFI_AP_STATE,
+ WIFI_AP_STATE_DISABLED);
+ final int prevState = intent.getIntExtra(EXTRA_PREVIOUS_WIFI_AP_STATE,
+ WIFI_AP_STATE_DISABLED);
+ final int errorCode = intent.getIntExtra(EXTRA_WIFI_AP_FAILURE_REASON,
+ HOTSPOT_NO_ERROR);
+ final String ifaceName =
+ intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);
+ final int mode = intent.getIntExtra(EXTRA_WIFI_AP_MODE,
+ WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ handleWifiApStateChange(currState, prevState, errorCode, ifaceName, mode);
+ }
+ },
+ new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION));
+
// Adding optimizations of only receiving broadcasts when wifi is enabled
// can result in race conditions when apps toggle wifi in the background
// without active user involvement. Always receive broadcasts.
@@ -412,6 +507,9 @@
registerForPackageOrUserRemoval();
mInIdleMode = mPowerManager.isDeviceIdleMode();
+ if (!mWifiStateMachine.syncInitialize(mWifiStateMachineChannel)) {
+ Log.wtf(TAG, "Failed to initialize WifiStateMachine");
+ }
mWifiController.start();
// If we are already disabled (could be due to airplane mode), avoid changing persist
@@ -429,18 +527,12 @@
mWifiStateMachine.handleUserSwitch(userId);
}
- /**
- * see {@link android.net.wifi.WifiManager#pingSupplicant()}
- * @return {@code true} if the operation succeeds, {@code false} otherwise
- */
- public boolean pingSupplicant() {
- enforceAccessPermission();
- if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncPingSupplicant(mWifiStateMachineChannel);
- } else {
- Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return false;
- }
+ public void handleUserUnlock(int userId) {
+ mWifiStateMachine.handleUserUnlock(userId);
+ }
+
+ public void handleUserStop(int userId) {
+ mWifiStateMachine.handleUserStop(userId);
}
/**
@@ -449,22 +541,38 @@
*
* @param settings If null, use default parameter, i.e. full scan.
* @param workSource If null, all blame is given to the calling uid.
+ * @param packageName Package name of the app that requests wifi scan.
*/
- public void startScan(ScanSettings settings, WorkSource workSource) {
+ @Override
+ public void startScan(ScanSettings settings, WorkSource workSource, String packageName) {
enforceChangePermission();
+
+ mLog.trace("startScan uid=%").c(Binder.getCallingUid()).flush();
+ // Check and throttle background apps for wifi scan.
+ if (isRequestFromBackground(packageName)) {
+ long lastScanMs = mLastScanTimestamps.getOrDefault(packageName, 0L);
+ long elapsedRealtime = mClock.getElapsedSinceBootMillis();
+
+ if (lastScanMs != 0 && (elapsedRealtime - lastScanMs) < mBackgroundThrottleInterval) {
+ sendFailedScanBroadcast();
+ return;
+ }
+ // Proceed with the scan request and record the time.
+ mLastScanTimestamps.put(packageName, elapsedRealtime);
+ }
synchronized (this) {
+ if (mWifiScanner == null) {
+ mWifiScanner = mWifiInjector.getWifiScanner();
+ }
if (mInIdleMode) {
// Need to send an immediate scan result broadcast in case the
// caller is waiting for a result ..
- // clear calling identity to send broadcast
- long callingIdentity = Binder.clearCallingIdentity();
- try {
- mWifiStateMachine.sendScanResultsAvailableBroadcast(/* scanSucceeded = */ false);
- } finally {
- // restore calling identity
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ // TODO: investigate if the logic to cancel scans when idle can move to
+ // WifiScanningServiceImpl. This will 1 - clean up WifiServiceImpl and 2 -
+ // avoid plumbing an awkward path to report a cancelled/failed scan. This will
+ // be sent directly until b/31398592 is fixed.
+ sendFailedScanBroadcast();
mScanPending = true;
return;
}
@@ -489,9 +597,52 @@
settings, workSource);
}
- public String getWpsNfcConfigurationToken(int netId) {
+ // Send a failed scan broadcast to indicate the current scan request failed.
+ private void sendFailedScanBroadcast() {
+ // clear calling identity to send broadcast
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ // restore calling identity
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+
+ }
+
+ // Check if the request comes from background.
+ private boolean isRequestFromBackground(String packageName) {
+ // Requests from system or wifi are not background.
+ if (Binder.getCallingUid() == Process.SYSTEM_UID
+ || Binder.getCallingUid() == Process.WIFI_UID) {
+ return false;
+ }
+ mAppOps.checkPackage(Binder.getCallingUid(), packageName);
+ if (mBackgroundThrottlePackageWhitelist.contains(packageName)) {
+ return false;
+ }
+
+ // getPackageImportance requires PACKAGE_USAGE_STATS permission, so clearing the incoming
+ // identify so the permission check can be done on system process where wifi runs in.
+ long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ return mActivityManager.getPackageImportance(packageName)
+ > BACKGROUND_IMPORTANCE_CUTOFF;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public String getCurrentNetworkWpsNfcConfigurationToken() {
enforceConnectivityInternalPermission();
- return mWifiStateMachine.syncGetWpsNfcConfigurationToken(netId);
+ mLog.trace("getCurrentNetworkWpsNfcConfigurationToken uid=%")
+ .c(Binder.getCallingUid()).flush();
+ // TODO Add private logging for netId b/33807876
+ return mWifiStateMachine.syncGetCurrentNetworkWpsNfcConfigurationToken();
}
boolean mInIdleMode;
@@ -513,10 +664,21 @@
}
if (doScan) {
// Someone requested a scan while we were idle; do a full scan now.
- startScan(null, null);
+ // The package name doesn't matter as the request comes from System UID.
+ startScan(null, null, "");
}
}
+ private void enforceNetworkSettingsPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS,
+ "WifiService");
+ }
+
+ private void enforceNetworkStackPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK,
+ "WifiService");
+ }
+
private void enforceAccessPermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
"WifiService");
@@ -555,17 +717,39 @@
"ConnectivityService");
}
+ private void enforceLocationPermission(String pkgName, int uid) {
+ mWifiPermissionsUtil.enforceLocationPermission(pkgName, uid);
+ }
+
+ private boolean checkNetworkSettingsPermission() {
+ int result = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_SETTINGS);
+ return result == PackageManager.PERMISSION_GRANTED;
+ }
+
/**
* see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
* @param enable {@code true} to enable, {@code false} to disable.
* @return {@code true} if the enable/disable operation was
* started or is already in the queue.
*/
+ @Override
public synchronized boolean setWifiEnabled(String packageName, boolean enable)
throws RemoteException {
enforceChangePermission();
Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
+ + ", uid=" + Binder.getCallingUid() + ", package=" + packageName);
+ mLog.trace("setWifiEnabled package=% uid=% enable=%").c(packageName)
+ .c(Binder.getCallingUid()).c(enable).flush();
+
+ // If SoftAp is enabled, only Settings is allowed to toggle wifi
+ boolean apEnabled =
+ mWifiStateMachine.syncGetWifiApState() != WifiManager.WIFI_AP_STATE_DISABLED;
+ boolean isFromSettings = checkNetworkSettingsPermission();
+ if (apEnabled && !isFromSettings) {
+ mLog.trace("setWifiEnabled SoftAp not disabled: only Settings can enable wifi").flush();
+ return false;
+ }
/*
* Caller might not have WRITE_SECURE_SETTINGS,
@@ -613,8 +797,10 @@
* {@link WifiManager#WIFI_STATE_ENABLING},
* {@link WifiManager#WIFI_STATE_UNKNOWN}
*/
+ @Override
public int getWifiEnabledState() {
enforceAccessPermission();
+ mLog.trace("getWifiEnabledState uid=%").c(Binder.getCallingUid()).flush();
return mWifiStateMachine.syncGetWifiState();
}
@@ -624,15 +810,21 @@
* part of WifiConfiguration
* @param enabled true to enable and false to disable
*/
+ @Override
public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
enforceChangePermission();
- ConnectivityManager.enforceTetherChangePermission(mContext);
+ mWifiPermissionsUtil.enforceTetherChangePermission(mContext);
+
+ mLog.trace("setWifiApEnabled uid=% enable=%").c(Binder.getCallingUid()).c(enabled).flush();
+
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
}
// null wifiConfig is a meaningful input for CMD_SET_AP
if (wifiConfig == null || isValid(wifiConfig)) {
- mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget();
+ int mode = WifiManager.IFACE_IP_MODE_UNSPECIFIED;
+ SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig);
+ mWifiController.sendMessage(CMD_SET_AP, enabled ? 1 : 0, 0, softApConfig);
} else {
Slog.e(TAG, "Invalid WifiConfiguration");
}
@@ -646,45 +838,501 @@
* {@link WifiManager#WIFI_AP_STATE_ENABLING},
* {@link WifiManager#WIFI_AP_STATE_FAILED}
*/
+ @Override
public int getWifiApEnabledState() {
enforceAccessPermission();
+ mLog.trace("getWifiApEnabledState uid=%").c(Binder.getCallingUid()).flush();
return mWifiStateMachine.syncGetWifiApState();
}
/**
+ * see {@link android.net.wifi.WifiManager#updateInterfaceIpState(String, int)}
+ *
+ * The possible modes include: {@link WifiManager#IFACE_IP_MODE_TETHERED},
+ * {@link WifiManager#IFACE_IP_MODE_LOCAL_ONLY},
+ * {@link WifiManager#IFACE_IP_MODE_CONFIGURATION_ERROR}
+ *
+ * @param ifaceName String name of the updated interface
+ * @param mode new operating mode of the interface
+ *
+ * @throws SecurityException if the caller does not have permission to call update
+ */
+ @Override
+ public void updateInterfaceIpState(String ifaceName, int mode) {
+ // NETWORK_STACK is a signature only permission.
+ enforceNetworkStackPermission();
+
+ // hand off the work to our handler thread
+ mClientHandler.post(() -> {
+ updateInterfaceIpStateInternal(ifaceName, mode);
+ });
+ }
+
+ private void updateInterfaceIpStateInternal(String ifaceName, int mode) {
+ // update interface IP state related to tethering and hotspot
+ synchronized (mLocalOnlyHotspotRequests) {
+ // update the mode tracker here - we clear out state below
+ Integer previousMode = WifiManager.IFACE_IP_MODE_UNSPECIFIED;
+ if (ifaceName != null) {
+ previousMode = mIfaceIpModes.put(ifaceName, mode);
+ }
+ Slog.d(TAG, "updateInterfaceIpState: ifaceName=" + ifaceName + " mode=" + mode
+ + " previous mode= " + previousMode);
+
+ switch (mode) {
+ case WifiManager.IFACE_IP_MODE_LOCAL_ONLY:
+ // first make sure we have registered requests.. otherwise clean up
+ if (mLocalOnlyHotspotRequests.isEmpty()) {
+ // we don't have requests... stop the hotspot
+ stopSoftAp();
+ updateInterfaceIpStateInternal(null, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ return;
+ }
+ // LOHS is ready to go! Call our registered requestors!
+ sendHotspotStartedMessageToAllLOHSRequestInfoEntriesLocked();
+ break;
+ case WifiManager.IFACE_IP_MODE_TETHERED:
+ // we have tethered an interface. we don't really act on this now other than if
+ // we have LOHS requests, and this is an issue. return incompatible mode for
+ // onFailed for the registered requestors since this can result from a race
+ // between a tether request and a hotspot request (tethering wins).
+ sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked(
+ LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE);
+ break;
+ case WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR:
+ // there was an error setting up the hotspot... trigger onFailed for the
+ // registered LOHS requestors
+ sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked(
+ LocalOnlyHotspotCallback.ERROR_GENERIC);
+ updateInterfaceIpStateInternal(null, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ break;
+ case WifiManager.IFACE_IP_MODE_UNSPECIFIED:
+ if (ifaceName == null) {
+ // interface name is null, this is due to softap teardown. clear all
+ // entries for now.
+ // TODO: Deal with individual interfaces when we receive updates for them
+ mIfaceIpModes.clear();
+ return;
+ }
+ break;
+ default:
+ mLog.trace("updateInterfaceIpStateInternal: unknown mode %").c(mode).flush();
+ }
+ }
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#startSoftAp(WifiConfiguration)}
+ * @param wifiConfig SSID, security and channel details as part of WifiConfiguration
+ * @return {@code true} if softap start was triggered
+ * @throws SecurityException if the caller does not have permission to start softap
+ */
+ @Override
+ public boolean startSoftAp(WifiConfiguration wifiConfig) {
+ // NETWORK_STACK is a signature only permission.
+ enforceNetworkStackPermission();
+
+ mLog.trace("startSoftAp uid=%").c(Binder.getCallingUid()).flush();
+
+ synchronized (mLocalOnlyHotspotRequests) {
+ // If a tethering request comes in while we have LOHS running (or requested), call stop
+ // for softap mode and restart softap with the tethering config.
+ if (!mLocalOnlyHotspotRequests.isEmpty()) {
+ stopSoftApInternal();
+ }
+
+ return startSoftApInternal(wifiConfig, WifiManager.IFACE_IP_MODE_TETHERED);
+ }
+ }
+
+ /**
+ * Internal method to start softap mode. Callers of this method should have already checked
+ * proper permissions beyond the NetworkStack permission.
+ */
+ private boolean startSoftApInternal(WifiConfiguration wifiConfig, int mode) {
+ mLog.trace("startSoftApInternal uid=% mode=%")
+ .c(Binder.getCallingUid()).c(mode).flush();
+
+ // null wifiConfig is a meaningful input for CMD_SET_AP
+ if (wifiConfig == null || isValid(wifiConfig)) {
+ SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig);
+ mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig);
+ return true;
+ }
+ Slog.e(TAG, "Invalid WifiConfiguration");
+ return false;
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#stopSoftAp()}
+ * @return {@code true} if softap stop was triggered
+ * @throws SecurityException if the caller does not have permission to stop softap
+ */
+ @Override
+ public boolean stopSoftAp() {
+ // NETWORK_STACK is a signature only permission.
+ enforceNetworkStackPermission();
+
+ // only permitted callers are allowed to this point - they must have gone through
+ // connectivity service since this method is protected with the NETWORK_STACK PERMISSION
+
+ mLog.trace("stopSoftAp uid=%").c(Binder.getCallingUid()).flush();
+
+ synchronized (mLocalOnlyHotspotRequests) {
+ // If a tethering request comes in while we have LOHS running (or requested), call stop
+ // for softap mode and restart softap with the tethering config.
+ if (!mLocalOnlyHotspotRequests.isEmpty()) {
+ mLog.trace("Call to stop Tethering while LOHS is active,"
+ + " Registered LOHS callers will be updated when softap stopped.");
+ }
+
+ return stopSoftApInternal();
+ }
+ }
+
+ /**
+ * Internal method to stop softap mode. Callers of this method should have already checked
+ * proper permissions beyond the NetworkStack permission.
+ */
+ private boolean stopSoftApInternal() {
+ mLog.trace("stopSoftApInternal uid=%").c(Binder.getCallingUid()).flush();
+
+ mWifiController.sendMessage(CMD_SET_AP, 0, 0);
+ return true;
+ }
+
+ /**
+ * Private method to handle SoftAp state changes
+ */
+ private void handleWifiApStateChange(
+ int currentState, int previousState, int errorCode, String ifaceName, int mode) {
+ // The AP state update from WifiStateMachine for softap
+ Slog.d(TAG, "handleWifiApStateChange: currentState=" + currentState
+ + " previousState=" + previousState + " errorCode= " + errorCode
+ + " ifaceName=" + ifaceName + " mode=" + mode);
+
+ // check if we have a failure - since it is possible (worst case scenario where
+ // WifiController and WifiStateMachine are out of sync wrt modes) to get two FAILED
+ // notifications in a row, we need to handle this first.
+ if (currentState == WIFI_AP_STATE_FAILED) {
+ // update registered LOHS callbacks if we see a failure
+ synchronized (mLocalOnlyHotspotRequests) {
+ int errorToReport = ERROR_GENERIC;
+ if (errorCode == SAP_START_FAILURE_NO_CHANNEL) {
+ errorToReport = ERROR_NO_CHANNEL;
+ }
+ // holding the required lock: send message to requestors and clear the list
+ sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked(
+ errorToReport);
+ // also need to clear interface ip state - send null for now since we don't know
+ // what interface (and we have one anyway)
+ updateInterfaceIpStateInternal(null, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ }
+ return;
+ }
+
+ if (currentState == WIFI_AP_STATE_DISABLING || currentState == WIFI_AP_STATE_DISABLED) {
+ // softap is shutting down or is down... let requestors know via the onStopped call
+ synchronized (mLocalOnlyHotspotRequests) {
+ // if we are currently in hotspot mode, then trigger onStopped for registered
+ // requestors, otherwise something odd happened and we should clear state
+ if (mIfaceIpModes.contains(WifiManager.IFACE_IP_MODE_LOCAL_ONLY)) {
+ // holding the required lock: send message to requestors and clear the list
+ sendHotspotStoppedMessageToAllLOHSRequestInfoEntriesLocked();
+ } else {
+ // LOHS not active: report an error (still holding the required lock)
+ sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked(ERROR_GENERIC);
+ }
+ // also clear interface ip state - send null for now since we don't know what
+ // interface (and we only have one anyway)
+ updateInterfaceIpState(null, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ }
+ return;
+ }
+
+ // remaining states are enabling or enabled... those are not used for the callbacks
+ }
+
+ /**
+ * Helper method to send a HOTSPOT_FAILED message to all registered LocalOnlyHotspotRequest
+ * callers and clear the registrations.
+ *
+ * Callers should already hold the mLocalOnlyHotspotRequests lock.
+ */
+ private void sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked(int arg1) {
+ for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) {
+ try {
+ requestor.sendHotspotFailedMessage(arg1);
+ requestor.unlinkDeathRecipient();
+ } catch (RemoteException e) {
+ // This will be cleaned up by binder death handling
+ }
+ }
+
+ // Since all callers were notified, now clear the registrations.
+ mLocalOnlyHotspotRequests.clear();
+ }
+
+ /**
+ * Helper method to send a HOTSPOT_STOPPED message to all registered LocalOnlyHotspotRequest
+ * callers and clear the registrations.
+ *
+ * Callers should already hold the mLocalOnlyHotspotRequests lock.
+ */
+ private void sendHotspotStoppedMessageToAllLOHSRequestInfoEntriesLocked() {
+ for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) {
+ try {
+ requestor.sendHotspotStoppedMessage();
+ requestor.unlinkDeathRecipient();
+ } catch (RemoteException e) {
+ // This will be cleaned up by binder death handling
+ }
+ }
+
+ // Since all callers were notified, now clear the registrations.
+ mLocalOnlyHotspotRequests.clear();
+ }
+
+ /**
+ * Helper method to send a HOTSPOT_STARTED message to all registered LocalOnlyHotspotRequest
+ * callers.
+ *
+ * Callers should already hold the mLocalOnlyHotspotRequests lock.
+ */
+ private void sendHotspotStartedMessageToAllLOHSRequestInfoEntriesLocked() {
+ for (LocalOnlyHotspotRequestInfo requestor : mLocalOnlyHotspotRequests.values()) {
+ try {
+ requestor.sendHotspotStartedMessage(mLocalOnlyHotspotConfig);
+ } catch (RemoteException e) {
+ // This will be cleaned up by binder death handling
+ }
+ }
+ }
+
+ /**
+ * Temporary method used for testing while startLocalOnlyHotspot is not fully implemented. This
+ * method allows unit tests to register callbacks directly for testing mechanisms triggered by
+ * softap mode changes.
+ */
+ @VisibleForTesting
+ void registerLOHSForTest(int pid, LocalOnlyHotspotRequestInfo request) {
+ mLocalOnlyHotspotRequests.put(pid, request);
+ }
+
+ /**
+ * Method to start LocalOnlyHotspot. In this method, permissions, settings and modes are
+ * checked to verify that we can enter softapmode. This method returns
+ * {@link LocalOnlyHotspotCallback#REQUEST_REGISTERED} if we will attempt to start, otherwise,
+ * possible startup erros may include tethering being disallowed failure reason {@link
+ * LocalOnlyHotspotCallback#ERROR_TETHERING_DISALLOWED} or an incompatible mode failure reason
+ * {@link LocalOnlyHotspotCallback#ERROR_INCOMPATIBLE_MODE}.
+ *
+ * see {@link WifiManager#startLocalOnlyHotspot(LocalOnlyHotspotCallback)}
+ *
+ * @param messenger Messenger to send messages to the corresponding WifiManager.
+ * @param binder IBinder instance to allow cleanup if the app dies
+ * @param packageName String name of the calling package
+ *
+ * @return int return code for attempt to start LocalOnlyHotspot.
+ *
+ * @throws SecurityException if the caller does not have permission to start a Local Only
+ * Hotspot.
+ * @throws IllegalStateException if the caller attempts to start the LocalOnlyHotspot while they
+ * have an outstanding request.
+ */
+ @Override
+ public int startLocalOnlyHotspot(Messenger messenger, IBinder binder, String packageName) {
+ // first check if the caller has permission to start a local only hotspot
+ // need to check for WIFI_STATE_CHANGE and location permission
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+
+ enforceChangePermission();
+ enforceLocationPermission(packageName, uid);
+ // also need to verify that Locations services are enabled.
+ if (mSettingsStore.getLocationModeSetting(mContext) == Settings.Secure.LOCATION_MODE_OFF) {
+ throw new SecurityException("Location mode is not enabled.");
+ }
+
+ // verify that tethering is not disabled
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
+ return LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
+ }
+
+ // the app should be in the foreground
+ try {
+ if (!mFrameworkFacade.isAppForeground(uid)) {
+ return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
+ }
+ } catch (RemoteException e) {
+ mLog.trace("RemoteException during isAppForeground when calling startLOHS");
+ return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
+ }
+
+ mLog.trace("startLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush();
+
+ synchronized (mLocalOnlyHotspotRequests) {
+ // check if we are currently tethering
+ if (mIfaceIpModes.contains(WifiManager.IFACE_IP_MODE_TETHERED)) {
+ // Tethering is enabled, cannot start LocalOnlyHotspot
+ mLog.trace("Cannot start localOnlyHotspot when WiFi Tethering is active.");
+ return LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
+ }
+
+ // does this caller already have a request?
+ LocalOnlyHotspotRequestInfo request = mLocalOnlyHotspotRequests.get(pid);
+ if (request != null) {
+ mLog.trace("caller already has an active request");
+ throw new IllegalStateException(
+ "Caller already has an active LocalOnlyHotspot request");
+ }
+
+ // now create the new LOHS request info object
+ request = new LocalOnlyHotspotRequestInfo(binder, messenger,
+ new LocalOnlyRequestorCallback());
+
+ // check current operating state and take action if needed
+ if (mIfaceIpModes.contains(WifiManager.IFACE_IP_MODE_LOCAL_ONLY)) {
+ // LOHS is already active, send out what is running
+ try {
+ mLog.trace("LOHS already up, trigger onStarted callback");
+ request.sendHotspotStartedMessage(mLocalOnlyHotspotConfig);
+ } catch (RemoteException e) {
+ return LocalOnlyHotspotCallback.ERROR_GENERIC;
+ }
+ } else if (mLocalOnlyHotspotRequests.isEmpty()) {
+ // this is the first request, then set up our config and start LOHS
+ mLocalOnlyHotspotConfig =
+ WifiApConfigStore.generateLocalOnlyHotspotConfig(mContext);
+ startSoftApInternal(mLocalOnlyHotspotConfig, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
+ }
+
+ mLocalOnlyHotspotRequests.put(pid, request);
+ return LocalOnlyHotspotCallback.REQUEST_REGISTERED;
+ }
+ }
+
+ /**
+ * see {@link WifiManager#stopLocalOnlyHotspot()}
+ *
+ * @throws SecurityException if the caller does not have permission to stop a Local Only
+ * Hotspot.
+ */
+ @Override
+ public void stopLocalOnlyHotspot() {
+ // first check if the caller has permission to stop a local only hotspot
+ enforceChangePermission();
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+
+ mLog.trace("stopLocalOnlyHotspot uid=% pid=%").c(uid).c(pid).flush();
+
+ synchronized (mLocalOnlyHotspotRequests) {
+ // was the caller already registered? check request tracker - return false if not
+ LocalOnlyHotspotRequestInfo requestInfo = mLocalOnlyHotspotRequests.get(pid);
+ if (requestInfo == null) {
+ return;
+ }
+ requestInfo.unlinkDeathRecipient();
+ unregisterCallingAppAndStopLocalOnlyHotspot(requestInfo);
+ } // end synchronized
+ }
+
+ /**
+ * Helper method to unregister LocalOnlyHotspot requestors and stop the hotspot if needed.
+ */
+ private void unregisterCallingAppAndStopLocalOnlyHotspot(LocalOnlyHotspotRequestInfo request) {
+ mLog.trace("unregisterCallingAppAndStopLocalOnlyHotspot pid=%").c(request.getPid()).flush();
+
+ synchronized (mLocalOnlyHotspotRequests) {
+ if (mLocalOnlyHotspotRequests.remove(request.getPid()) == null) {
+ mLog.trace("LocalOnlyHotspotRequestInfo not found to remove");
+ return;
+ }
+
+ if (mLocalOnlyHotspotRequests.isEmpty()) {
+ mLocalOnlyHotspotConfig = null;
+ updateInterfaceIpStateInternal(null, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ // if that was the last caller, then call stopSoftAp as WifiService
+ long identity = Binder.clearCallingIdentity();
+ try {
+ stopSoftApInternal();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+ }
+
+ /**
+ * see {@link WifiManager#watchLocalOnlyHotspot(LocalOnlyHotspotObserver)}
+ *
+ * This call requires the android.permission.NETWORK_SETTINGS permission.
+ *
+ * @param messenger Messenger to send messages to the corresponding WifiManager.
+ * @param binder IBinder instance to allow cleanup if the app dies
+ *
+ * @throws SecurityException if the caller does not have permission to watch Local Only Hotspot
+ * status updates.
+ * @throws IllegalStateException if the caller attempts to watch LocalOnlyHotspot updates with
+ * an existing subscription.
+ */
+ @Override
+ public void startWatchLocalOnlyHotspot(Messenger messenger, IBinder binder) {
+ final String packageName = mContext.getOpPackageName();
+
+ // NETWORK_SETTINGS is a signature only permission.
+ enforceNetworkSettingsPermission();
+
+ throw new UnsupportedOperationException("LocalOnlyHotspot is still in development");
+ }
+
+ /**
+ * see {@link WifiManager#unregisterLocalOnlyHotspotObserver()}
+ */
+ @Override
+ public void stopWatchLocalOnlyHotspot() {
+ // NETWORK_STACK is a signature only permission.
+ enforceNetworkSettingsPermission();
+ throw new UnsupportedOperationException("LocalOnlyHotspot is still in development");
+ }
+
+ /**
* see {@link WifiManager#getWifiApConfiguration()}
* @return soft access point configuration
+ * @throws SecurityException if the caller does not have permission to retrieve the softap
+ * config
*/
+ @Override
public WifiConfiguration getWifiApConfiguration() {
enforceAccessPermission();
+ int uid = Binder.getCallingUid();
+ // only allow Settings UI to get the saved SoftApConfig
+ if (!mWifiPermissionsUtil.checkConfigOverridePermission(uid)) {
+ // random apps should not be allowed to read the user specified config
+ throw new SecurityException("App not allowed to read or update stored WiFi Ap config "
+ + "(uid = " + uid + ")");
+ }
+ mLog.trace("getWifiApConfiguration uid=%").c(uid).flush();
return mWifiStateMachine.syncGetWifiApConfiguration();
}
/**
- * see {@link WifiManager#buildWifiConfig()}
- * @return a WifiConfiguration.
- */
- public WifiConfiguration buildWifiConfig(String uriString, String mimeType, byte[] data) {
- if (mimeType.equals(ConfigBuilder.WifiConfigType)) {
- try {
- return ConfigBuilder.buildConfig(uriString, data, mContext);
- }
- catch (IOException | GeneralSecurityException | SAXException e) {
- Log.e(TAG, "Failed to parse wi-fi configuration: " + e);
- }
- }
- else {
- Log.i(TAG, "Unknown wi-fi config type: " + mimeType);
- }
- return null;
- }
-
- /**
* see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
* @param wifiConfig WifiConfiguration details for soft access point
+ * @throws SecurityException if the caller does not have permission to write the sotap config
*/
+ @Override
public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
enforceChangePermission();
+ int uid = Binder.getCallingUid();
+ // only allow Settings UI to write the stored SoftApConfig
+ if (!mWifiPermissionsUtil.checkConfigOverridePermission(uid)) {
+ // random apps should not be allowed to read the user specified config
+ throw new SecurityException("App not allowed to read or update stored WiFi AP config "
+ + "(uid = " + uid + ")");
+ }
+ mLog.trace("setWifiApConfiguration uid=%").c(uid).flush();
if (wifiConfig == null)
return;
if (isValid(wifiConfig)) {
@@ -695,44 +1343,52 @@
}
/**
- * @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();
+ mLog.trace("isScanAlwaysAvailable uid=%").c(Binder.getCallingUid()).flush();
return mSettingsStore.isScanAlwaysAvailable();
}
/**
* see {@link android.net.wifi.WifiManager#disconnect()}
*/
+ @Override
public void disconnect() {
enforceChangePermission();
+ mLog.trace("disconnect uid=%").c(Binder.getCallingUid()).flush();
mWifiStateMachine.disconnectCommand();
}
/**
* see {@link android.net.wifi.WifiManager#reconnect()}
*/
+ @Override
public void reconnect() {
enforceChangePermission();
+ mLog.trace("reconnect uid=%").c(Binder.getCallingUid()).flush();
mWifiStateMachine.reconnectCommand();
}
/**
* see {@link android.net.wifi.WifiManager#reassociate()}
*/
+ @Override
public void reassociate() {
enforceChangePermission();
+ mLog.trace("reassociate uid=%").c(Binder.getCallingUid()).flush();
mWifiStateMachine.reassociateCommand();
}
/**
* see {@link android.net.wifi.WifiManager#getSupportedFeatures}
*/
+ @Override
public int getSupportedFeatures() {
enforceAccessPermission();
+ mLog.trace("getSupportedFeatures uid=%").c(Binder.getCallingUid()).flush();
if (mWifiStateMachineChannel != null) {
return mWifiStateMachine.syncGetSupportedFeatures(mWifiStateMachineChannel);
} else {
@@ -744,6 +1400,7 @@
@Override
public void requestActivityInfo(ResultReceiver result) {
Bundle bundle = new Bundle();
+ mLog.trace("requestActivityInfo uid=%").c(Binder.getCallingUid()).flush();
bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, reportActivityInfo());
result.send(0, bundle);
}
@@ -751,8 +1408,10 @@
/**
* see {@link android.net.wifi.WifiManager#getControllerActivityEnergyInfo(int)}
*/
+ @Override
public WifiActivityEnergyInfo reportActivityInfo() {
enforceAccessPermission();
+ mLog.trace("reportActivityInfo uid=%").c(Binder.getCallingUid()).flush();
if ((getSupportedFeatures() & WifiManager.WIFI_FEATURE_LINK_LAYER_STATS) == 0) {
return null;
}
@@ -803,7 +1462,7 @@
}
// Convert the LinkLayerStats into EnergyActivity
- energyInfo = new WifiActivityEnergyInfo(SystemClock.elapsedRealtime(),
+ energyInfo = new WifiActivityEnergyInfo(mClock.getElapsedSinceBootMillis(),
WifiActivityEnergyInfo.STACK_STATE_STATE_IDLE, stats.tx_time,
txTimePerLevel, stats.rx_time, rxIdleTime, energyUsed);
}
@@ -822,54 +1481,57 @@
* see {@link android.net.wifi.WifiManager#getConfiguredNetworks()}
* @return the list of configured networks
*/
- public List<WifiConfiguration> getConfiguredNetworks() {
+ @Override
+ public ParceledListSlice<WifiConfiguration> getConfiguredNetworks() {
enforceAccessPermission();
+ mLog.trace("getConfiguredNetworks uid=%").c(Binder.getCallingUid()).flush();
if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncGetConfiguredNetworks(Binder.getCallingUid(),
- mWifiStateMachineChannel);
+ List<WifiConfiguration> configs = mWifiStateMachine.syncGetConfiguredNetworks(
+ Binder.getCallingUid(), mWifiStateMachineChannel);
+ if (configs != null) {
+ return new ParceledListSlice<WifiConfiguration>(configs);
+ }
} else {
Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return null;
}
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#getHasCarrierNetworks()}
- * @return if Carrier Networks have been configured
- */
- public boolean hasCarrierConfiguredNetworks() {
- enforceAccessPermission();
- if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncHasCarrierConfiguredNetworks(Binder.getCallingUid(),
- mWifiStateMachineChannel);
- } else {
- Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return false;
- }
+ return null;
}
/**
* see {@link android.net.wifi.WifiManager#getPrivilegedConfiguredNetworks()}
* @return the list of configured networks with real preSharedKey
*/
- public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
+ @Override
+ public ParceledListSlice<WifiConfiguration> getPrivilegedConfiguredNetworks() {
enforceReadCredentialPermission();
enforceAccessPermission();
+ mLog.trace("getPrivilegedConfiguredNetworks uid=%").c(Binder.getCallingUid()).flush();
if (mWifiStateMachineChannel != null) {
- return mWifiStateMachine.syncGetPrivilegedConfiguredNetwork(mWifiStateMachineChannel);
+ List<WifiConfiguration> configs =
+ mWifiStateMachine.syncGetPrivilegedConfiguredNetwork(mWifiStateMachineChannel);
+ if (configs != null) {
+ return new ParceledListSlice<WifiConfiguration>(configs);
+ }
} else {
Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
- return null;
}
+ return null;
}
/**
- * Returns a WifiConfiguration matching this ScanResult
+ * Returns a WifiConfiguration for a Passpoint network matching this ScanResult.
+ *
* @param scanResult scanResult that represents the BSSID
* @return {@link WifiConfiguration} that matches this BSSID or null
*/
+ @Override
public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
enforceAccessPermission();
+ mLog.trace("getMatchingWifiConfig uid=%").c(Binder.getCallingUid()).flush();
+ if (!mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ throw new UnsupportedOperationException("Passpoint not enabled");
+ }
return mWifiStateMachine.syncGetMatchingWifiConfig(scanResult, mWifiStateMachineChannel);
}
@@ -878,35 +1540,36 @@
* @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)) {
+ mLog.trace("addOrUpdateNetwork uid=%").c(Binder.getCallingUid()).flush();
- WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
-
- if (config.isPasspoint() &&
- (enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS ||
- enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS)) {
- if (config.updateIdentifier != null) {
- enforceAccessPermission();
- }
- else {
- try {
- verifyCert(enterpriseConfig.getCaCertificate());
- } catch (CertPathValidatorException cpve) {
- Slog.e(TAG, "CA Cert " +
- enterpriseConfig.getCaCertificate().getSubjectX500Principal() +
- " untrusted: " + cpve.getMessage());
- return -1;
- } catch (GeneralSecurityException | IOException e) {
- Slog.e(TAG, "Failed to verify certificate" +
- enterpriseConfig.getCaCertificate().getSubjectX500Principal() +
- ": " + e);
- return -1;
- }
- }
+ // Previously, this API is overloaded for installing Passpoint profiles. Now
+ // that we have a dedicated API for doing it, redirect the call to the dedicated API.
+ if (config.isPasspoint()) {
+ PasspointConfiguration passpointConfig =
+ PasspointProvider.convertFromWifiConfig(config);
+ if (passpointConfig.getCredential() == null) {
+ Slog.e(TAG, "Missing credential for Passpoint profile");
+ return -1;
}
+ // Copy over certificates and keys.
+ passpointConfig.getCredential().setCaCertificate(
+ config.enterpriseConfig.getCaCertificate());
+ passpointConfig.getCredential().setClientCertificateChain(
+ config.enterpriseConfig.getClientCertificateChain());
+ passpointConfig.getCredential().setClientPrivateKey(
+ config.enterpriseConfig.getClientPrivateKey());
+ if (!addOrUpdatePasspointConfiguration(passpointConfig)) {
+ Slog.e(TAG, "Failed to add Passpoint profile");
+ return -1;
+ }
+ // There is no network ID associated with a Passpoint profile.
+ return 0;
+ }
+ if (isValid(config)) {
//TODO: pass the Uid the WifiStateMachine as a message parameter
Slog.i("addOrUpdateNetwork", " uid = " + Integer.toString(Binder.getCallingUid())
+ " SSID " + config.SSID
@@ -948,9 +1611,11 @@
* to the supplicant
* @return {@code true} if the operation succeeded
*/
+ @Override
public boolean removeNetwork(int netId) {
enforceChangePermission();
-
+ mLog.trace("removeNetwork uid=%").c(Binder.getCallingUid()).flush();
+ // TODO Add private logging for netId b/33807876
if (mWifiStateMachineChannel != null) {
return mWifiStateMachine.syncRemoveNetwork(mWifiStateMachineChannel, netId);
} else {
@@ -966,8 +1631,14 @@
* @param disableOthers if true, disable all other networks.
* @return {@code true} if the operation succeeded
*/
+ @Override
public boolean enableNetwork(int netId, boolean disableOthers) {
enforceChangePermission();
+ // TODO b/33807876 Log netId
+ mLog.trace("enableNetwork uid=% disableOthers=%")
+ .c(Binder.getCallingUid())
+ .c(disableOthers).flush();
+
if (mWifiStateMachineChannel != null) {
return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId,
disableOthers);
@@ -983,8 +1654,12 @@
* to the supplicant
* @return {@code true} if the operation succeeded
*/
+ @Override
public boolean disableNetwork(int netId) {
enforceChangePermission();
+ // TODO b/33807876 Log netId
+ mLog.trace("disableNetwork uid=%").c(Binder.getCallingUid()).flush();
+
if (mWifiStateMachineChannel != null) {
return mWifiStateMachine.syncDisableNetwork(mWifiStateMachineChannel, netId);
} else {
@@ -997,8 +1672,10 @@
* See {@link android.net.wifi.WifiManager#getConnectionInfo()}
* @return the Wi-Fi information, contained in {@link WifiInfo}.
*/
+ @Override
public WifiInfo getConnectionInfo() {
enforceAccessPermission();
+ mLog.trace("getConnectionInfo uid=%").c(Binder.getCallingUid()).flush();
/*
* Make sure we have the latest information, by sending
* a status request to the supplicant.
@@ -1011,54 +1688,76 @@
* a list of {@link ScanResult} objects.
* @return the list of results
*/
+ @Override
public List<ScanResult> getScanResults(String callingPackage) {
enforceAccessPermission();
- int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
- boolean canReadPeerMacAddresses = checkPeersMacAddress();
- boolean isActiveNetworkScorer =
- NetworkScorerAppManager.isCallerActiveScorer(mContext, uid);
- boolean hasInteractUsersFull = checkInteractAcrossUsersFull();
long ident = Binder.clearCallingIdentity();
try {
- if (!canReadPeerMacAddresses && !isActiveNetworkScorer
- && !isLocationEnabled(callingPackage)) {
+ if (!mWifiPermissionsUtil.canAccessScanResults(callingPackage,
+ uid, Build.VERSION_CODES.M)) {
return new ArrayList<ScanResult>();
}
- if (!canReadPeerMacAddresses && !isActiveNetworkScorer
- && !checkCallerCanAccessScanResults(callingPackage, uid)) {
- return new ArrayList<ScanResult>();
+ if (mWifiScanner == null) {
+ mWifiScanner = mWifiInjector.getWifiScanner();
}
- if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
- return new ArrayList<ScanResult>();
- }
- if (!isCurrentProfile(userId) && !hasInteractUsersFull) {
- return new ArrayList<ScanResult>();
- }
- return mWifiStateMachine.syncGetScanResultsList();
+ return mWifiScanner.getSingleScanResults();
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
- * Add a Hotspot 2.0 release 2 Management Object
- * @param mo The MO in XML form
- * @return -1 for failure
+ * Add or update a Passpoint configuration.
+ *
+ * @param config The Passpoint configuration to be added
+ * @return true on success or false on failure
*/
- public int addPasspointManagementObject(String mo) {
- return mWifiStateMachine.syncAddPasspointManagementObject(mWifiStateMachineChannel, mo);
+ @Override
+ public boolean addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
+ enforceChangePermission();
+ mLog.trace("addorUpdatePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush();
+ if (!mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ throw new UnsupportedOperationException("Passpoint not enabled");
+ }
+ return mWifiStateMachine.syncAddOrUpdatePasspointConfig(mWifiStateMachineChannel, config,
+ Binder.getCallingUid());
}
/**
- * Modify a Hotspot 2.0 release 2 Management Object
- * @param fqdn The FQDN of the service provider
- * @param mos A List of MO definitions to be updated
- * @return the number of nodes updated, or -1 for failure
+ * Remove the Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
+ *
+ * @param fqdn The FQDN of the Passpoint configuration to be removed
+ * @return true on success or false on failure
*/
- public int modifyPasspointManagementObject(String fqdn, List<PasspointManagementObjectDefinition> mos) {
- return mWifiStateMachine.syncModifyPasspointManagementObject(mWifiStateMachineChannel, fqdn, mos);
+ @Override
+ public boolean removePasspointConfiguration(String fqdn) {
+ enforceChangePermission();
+ mLog.trace("removePasspointConfiguration uid=%").c(Binder.getCallingUid()).flush();
+ if (!mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ throw new UnsupportedOperationException("Passpoint not enabled");
+ }
+ return mWifiStateMachine.syncRemovePasspointConfig(mWifiStateMachineChannel, fqdn);
+ }
+
+ /**
+ * Return the list of the installed Passpoint configurations.
+ *
+ * An empty list will be returned when no configuration is installed.
+ *
+ * @return A list of {@link PasspointConfiguration}
+ */
+ @Override
+ public List<PasspointConfiguration> getPasspointConfigurations() {
+ enforceAccessPermission();
+ mLog.trace("getPasspointConfigurations uid=%").c(Binder.getCallingUid()).flush();
+ if (!mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ throw new UnsupportedOperationException("Passpoint not enabled");
+ }
+ return mWifiStateMachine.syncGetPasspointConfigs(mWifiStateMachineChannel);
}
/**
@@ -1066,7 +1765,14 @@
* @param bssid The BSSID of the AP
* @param fileName Icon file name
*/
+ @Override
public void queryPasspointIcon(long bssid, String fileName) {
+ enforceAccessPermission();
+ mLog.trace("queryPasspointIcon uid=%").c(Binder.getCallingUid()).flush();
+ if (!mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_wifi_hotspot2_enabled)) {
+ throw new UnsupportedOperationException("Passpoint not enabled");
+ }
mWifiStateMachine.syncQueryPasspointIcon(mWifiStateMachineChannel, bssid, fileName);
}
@@ -1075,7 +1781,9 @@
* @param fqdn FQDN of the SP
* @return ordinal [HomeProvider, RoamingProvider, Incomplete, None, Declined]
*/
+ @Override
public int matchProviderWithCurrentNetwork(String fqdn) {
+ mLog.trace("matchProviderWithCurrentNetwork uid=%").c(Binder.getCallingUid()).flush();
return mWifiStateMachine.matchProviderWithCurrentNetwork(mWifiStateMachineChannel, fqdn);
}
@@ -1084,62 +1792,22 @@
* @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) {
+ mLog.trace("deauthenticateNetwork uid=%").c(Binder.getCallingUid()).flush();
mWifiStateMachine.deauthenticateNetwork(mWifiStateMachineChannel, holdoff, ess);
}
- private boolean isLocationEnabled(String callingPackage) {
- boolean legacyForegroundApp = !isMApp(mContext, callingPackage)
- && isForegroundApp(callingPackage);
- return legacyForegroundApp || Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF)
- != Settings.Secure.LOCATION_MODE_OFF;
- }
-
- /**
- * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
- */
- private boolean checkInteractAcrossUsersFull() {
- return mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Returns true if the caller holds PEERS_MAC_ADDRESS.
- */
- private boolean checkPeersMacAddress() {
- return mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.PEERS_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED;
- }
-
- /**
- * Returns true if the calling user is the current one or a profile of the
- * current user..
- */
- private boolean isCurrentProfile(int userId) {
- int currentUser = ActivityManager.getCurrentUser();
- if (userId == currentUser) {
- return true;
- }
- List<UserInfo> profiles = mUserManager.getProfiles(currentUser);
- for (UserInfo user : profiles) {
- if (userId == user.id) {
- return true;
- }
- }
- return false;
- }
-
/**
* Tell the supplicant to persist the current list of configured networks.
* @return {@code true} if the operation succeeded
*
* TODO: deprecate this
*/
+ @Override
public boolean saveConfiguration() {
- boolean result = true;
enforceChangePermission();
+ mLog.trace("saveConfiguration uid=%").c(Binder.getCallingUid()).flush();
if (mWifiStateMachineChannel != null) {
return mWifiStateMachine.syncSaveConfig(mWifiStateMachineChannel);
} else {
@@ -1157,21 +1825,15 @@
* 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);
enforceConnectivityInternalPermission();
+ mLog.trace("setCountryCode uid=%").c(Binder.getCallingUid()).flush();
final long token = Binder.clearCallingIdentity();
- try {
- if (mCountryCode.setCountryCode(countryCode, persist) && persist) {
- // Save this country code to persistent storage
- mFacade.setStringSetting(mContext,
- Settings.Global.WIFI_COUNTRY_CODE,
- countryCode);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ mCountryCode.setCountryCode(countryCode);
+ Binder.restoreCallingIdentity(token);
}
/**
@@ -1180,44 +1842,18 @@
* not.
* Returns null when there is no country code available.
*/
+ @Override
public String getCountryCode() {
enforceConnectivityInternalPermission();
+ mLog.trace("getCountryCode uid=%").c(Binder.getCallingUid()).flush();
String country = mCountryCode.getCountryCode();
return country;
}
- /**
- * Set the operational frequency band
- * @param band One of
- * {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
- * {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
- * {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
- * @param persist {@code true} if the setting should be remembered.
- *
- */
- public void setFrequencyBand(int band, boolean persist) {
- enforceChangePermission();
- if (!isDualBandSupported()) return;
- Slog.i(TAG, "WifiService trying to set frequency band to " + band +
- " with persist set to " + persist);
- final long token = Binder.clearCallingIdentity();
- try {
- mWifiStateMachine.setFrequencyBand(band, persist);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Get the operational frequency band
- */
- public int getFrequencyBand() {
- enforceAccessPermission();
- return mWifiStateMachine.getFrequencyBand();
- }
-
+ @Override
public boolean isDualBandSupported() {
//TODO: Should move towards adding a driver API that checks at runtime
+ mLog.trace("isDualBandSupported uid=%").c(Binder.getCallingUid()).flush();
return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_wifi_dual_band_support);
}
@@ -1228,8 +1864,11 @@
* @return the DHCP information
* @deprecated
*/
+ @Override
+ @Deprecated
public DhcpInfo getDhcpInfo() {
enforceAccessPermission();
+ mLog.trace("getDhcpInfo uid=%").c(Binder.getCallingUid()).flush();
DhcpResults dhcpResults = mWifiStateMachine.syncGetDhcpResults();
DhcpInfo info = new DhcpInfo();
@@ -1264,26 +1903,6 @@
}
/**
- * see {@link android.net.wifi.WifiManager#addToBlacklist}
- *
- */
- public void addToBlacklist(String bssid) {
- enforceChangePermission();
-
- mWifiStateMachine.addToBlacklist(bssid);
- }
-
- /**
- * see {@link android.net.wifi.WifiManager#clearBlacklist}
- *
- */
- public void clearBlacklist() {
- enforceChangePermission();
-
- mWifiStateMachine.clearBlacklist();
- }
-
- /**
* enable TDLS for the local NIC to remote NIC
* The APPs don't know the remote MAC address to identify NIC though,
* so we need to do additional work to find it from remote IP address
@@ -1357,11 +1976,12 @@
}
}
+ @Override
public void enableTdls(String remoteAddress, boolean enable) {
if (remoteAddress == null) {
throw new IllegalArgumentException("remoteAddress cannot be null");
}
-
+ mLog.trace("enableTdls uid=% enable=%").c(Binder.getCallingUid()).c(enable).flush();
TdlsTaskParams params = new TdlsTaskParams();
params.remoteIpAddress = remoteAddress;
params.enable = enable;
@@ -1369,7 +1989,12 @@
}
+ @Override
public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
+ mLog.trace("enableTdlsWithMacAddress uid=% enable=%")
+ .c(Binder.getCallingUid())
+ .c(enable)
+ .flush();
if (remoteMacAddress == null) {
throw new IllegalArgumentException("remoteMacAddress cannot be null");
}
@@ -1381,29 +2006,25 @@
* Get a reference to handler. This is used by a client to establish
* an AsyncChannel communication with WifiService
*/
+ @Override
public Messenger getWifiServiceMessenger() {
enforceAccessPermission();
enforceChangePermission();
+ mLog.trace("getWifiServiceMessenger uid=%").c(Binder.getCallingUid()).flush();
return new Messenger(mClientHandler);
}
/**
* Disable an ephemeral network, i.e. network that is created thru a WiFi Scorer
*/
+ @Override
public void disableEphemeralNetwork(String SSID) {
enforceAccessPermission();
enforceChangePermission();
+ mLog.trace("disableEphemeralNetwork uid=%").c(Binder.getCallingUid()).flush();
mWifiStateMachine.disableEphemeralNetwork(SSID);
}
- /**
- * Get the IP and proxy configuration file
- */
- public String getConfigFile() {
- enforceAccessPermission();
- return mWifiStateMachine.getConfigFile();
- }
-
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1472,10 +2093,55 @@
mWifiController.sendMessage(CMD_SCAN_ALWAYS_MODE_CHANGED);
}
};
-
- mContext.getContentResolver().registerContentObserver(
+ mFrameworkFacade.registerContentObserver(mContext,
Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE),
false, contentObserver);
+
+ }
+
+ // Monitors settings changes related to background wifi scan throttling.
+ private void registerForBackgroundThrottleChanges() {
+ mFrameworkFacade.registerContentObserver(
+ mContext,
+ Settings.Global.getUriFor(
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS),
+ false,
+ new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateBackgroundThrottleInterval();
+ }
+ }
+ );
+ mFrameworkFacade.registerContentObserver(
+ mContext,
+ Settings.Global.getUriFor(
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
+ false,
+ new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateBackgroundThrottlingWhitelist();
+ }
+ }
+ );
+ }
+
+ private void updateBackgroundThrottleInterval() {
+ mBackgroundThrottleInterval = mFrameworkFacade.getLongSetting(
+ mContext,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
+ DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS);
+ }
+
+ private void updateBackgroundThrottlingWhitelist() {
+ String setting = mFrameworkFacade.getStringSetting(
+ mContext,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+ mBackgroundThrottlePackageWhitelist.clear();
+ if (setting != null) {
+ mBackgroundThrottlePackageWhitelist.addAll(Arrays.asList(setting.split(",")));
+ }
}
private void registerForBroadcasts() {
@@ -1538,72 +2204,59 @@
+ ", uid=" + Binder.getCallingUid());
return;
}
- if (args.length > 0 && WifiMetrics.PROTO_DUMP_ARG.equals(args[0])) {
+ if (args != null && args.length > 0 && WifiMetrics.PROTO_DUMP_ARG.equals(args[0])) {
// WifiMetrics proto bytes were requested. Dump only these.
mWifiStateMachine.updateWifiMetrics();
mWifiMetrics.dump(fd, pw, args);
- } else if (args.length > 0 && IpManager.DUMP_ARG.equals(args[0])) {
+ } else if (args != null && args.length > 0 && IpManager.DUMP_ARG.equals(args[0])) {
// IpManager dump was requested. Pass it along and take no further action.
String[] ipManagerArgs = new String[args.length - 1];
System.arraycopy(args, 1, ipManagerArgs, 0, ipManagerArgs.length);
mWifiStateMachine.dumpIpManager(fd, pw, ipManagerArgs);
+ } else if (args != null && args.length > 0
+ && DUMP_ARG_SET_IPREACH_DISCONNECT.equals(args[0])) {
+ if (args.length > 1) {
+ if (DUMP_ARG_SET_IPREACH_DISCONNECT_ENABLED.equals(args[1])) {
+ mWifiStateMachine.setIpReachabilityDisconnectEnabled(true);
+ } else if (DUMP_ARG_SET_IPREACH_DISCONNECT_DISABLED.equals(args[1])) {
+ mWifiStateMachine.setIpReachabilityDisconnectEnabled(false);
+ }
+ }
+ pw.println("IPREACH_DISCONNECT state is "
+ + mWifiStateMachine.getIpReachabilityDisconnectEnabled());
+ return;
} else {
pw.println("Wi-Fi is " + mWifiStateMachine.syncGetWifiStateByName());
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);
+ mFacade.getIntegerSetting(mContext,
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
pw.println("mInIdleMode " + mInIdleMode);
pw.println("mScanPending " + mScanPending);
mWifiController.dump(fd, pw, args);
mSettingsStore.dump(fd, pw, args);
mNotificationController.dump(fd, pw, args);
mTrafficPoller.dump(fd, pw, args);
-
- pw.println("Latest scan results:");
- List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
- long nowMs = System.currentTimeMillis();
- if (scanResults != null && scanResults.size() != 0) {
- pw.println(" BSSID Frequency RSSI Age SSID " +
- " Flags");
- for (ScanResult r : scanResults) {
- long ageSec = 0;
- long ageMilli = 0;
- if (nowMs > r.seen && r.seen > 0) {
- ageSec = (nowMs - r.seen) / 1000;
- ageMilli = (nowMs - r.seen) % 1000;
- }
- String candidate = " ";
- if (r.isAutoJoinCandidate > 0) candidate = "+";
- pw.printf(" %17s %9d %5d %3d.%03d%s %-32s %s\n",
- r.BSSID,
- r.frequency,
- r.level,
- ageSec, ageMilli,
- candidate,
- r.SSID == null ? "" : r.SSID,
- r.capabilities);
- }
- }
pw.println();
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();
+ mWifiStateMachine.updateWifiMetrics();
+ mWifiMetrics.dump(fd, pw, args);
+ pw.println();
+ mWifiBackupRestore.dump(fd, pw, args);
+ pw.println();
}
}
@Override
public boolean acquireWifiLock(IBinder binder, int lockMode, String tag, WorkSource ws) {
+ mLog.trace("acquireWifiLock uid=% lockMode=%")
+ .c(Binder.getCallingUid())
+ .c(lockMode).flush();
if (mWifiLockManager.acquireWifiLock(lockMode, tag, binder, ws)) {
mWifiController.sendMessage(CMD_LOCKS_CHANGED);
return true;
@@ -1613,11 +2266,13 @@
@Override
public void updateWifiLockWorkSource(IBinder binder, WorkSource ws) {
+ mLog.trace("updateWifiLockWorkSource uid=%").c(Binder.getCallingUid()).flush();
mWifiLockManager.updateWifiLockWorkSource(binder, ws);
}
@Override
public boolean releaseWifiLock(IBinder binder) {
+ mLog.trace("releaseWifiLock uid=%").c(Binder.getCallingUid()).flush();
if (mWifiLockManager.releaseWifiLock(binder)) {
mWifiController.sendMessage(CMD_LOCKS_CHANGED);
return true;
@@ -1625,171 +2280,117 @@
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();
- }
- }
+ mLog.trace("initializeMulticastFiltering uid=%").c(Binder.getCallingUid()).flush();
+ 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);
- }
+ mLog.trace("acquireMulticastLock uid=%").c(Binder.getCallingUid()).flush();
+ 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);
- }
- }
- }
+ mLog.trace("releaseMulticastLock uid=%").c(Binder.getCallingUid()).flush();
+ 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);
- }
+ mLog.trace("isMulticastEnabled uid=%").c(Binder.getCallingUid()).flush();
+ return mWifiMulticastLockManager.isMulticastEnabled();
}
+ @Override
public void enableVerboseLogging(int verbose) {
enforceAccessPermission();
+ mLog.trace("enableVerboseLogging uid=% verbose=%")
+ .c(Binder.getCallingUid())
+ .c(verbose).flush();
+ mFacade.setIntegerSetting(
+ mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, verbose);
+ enableVerboseLoggingInternal(verbose);
+ }
+
+ void enableVerboseLoggingInternal(int verbose) {
mWifiStateMachine.enableVerboseLogging(verbose);
mWifiLockManager.enableVerboseLogging(verbose);
+ mWifiMulticastLockManager.enableVerboseLogging(verbose);
+ mWifiInjector.getWifiLastResortWatchdog().enableVerboseLogging(verbose);
+ mWifiInjector.getWifiBackupRestore().enableVerboseLogging(verbose);
+ LogcatLog.enableVerboseLogging(verbose);
}
+ @Override
public int getVerboseLoggingLevel() {
enforceAccessPermission();
- return mWifiStateMachine.getVerboseLoggingLevel();
+ mLog.trace("getVerboseLoggingLevel uid=%").c(Binder.getCallingUid()).flush();
+ return mFacade.getIntegerSetting(
+ mContext, Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0);
}
+ @Override
public void enableAggressiveHandover(int enabled) {
enforceAccessPermission();
+ mLog.trace("enableAggressiveHandover uid=% enabled=%")
+ .c(Binder.getCallingUid())
+ .c(enabled)
+ .flush();
mWifiStateMachine.enableAggressiveHandover(enabled);
}
+ @Override
public int getAggressiveHandover() {
enforceAccessPermission();
+ mLog.trace("getAggressiveHandover uid=%").c(Binder.getCallingUid()).flush();
return mWifiStateMachine.getAggressiveHandover();
}
+ @Override
public void setAllowScansWithTraffic(int enabled) {
enforceAccessPermission();
+ mLog.trace("setAllowScansWithTraffic uid=% enabled=%")
+ .c(Binder.getCallingUid())
+ .c(enabled).flush();
mWifiStateMachine.setAllowScansWithTraffic(enabled);
}
+ @Override
public int getAllowScansWithTraffic() {
enforceAccessPermission();
+ mLog.trace("getAllowScansWithTraffic uid=%").c(Binder.getCallingUid()).flush();
return mWifiStateMachine.getAllowScansWithTraffic();
}
+ @Override
public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
enforceChangePermission();
+ mLog.trace("setEnableAutoJoinWhenAssociated uid=% enabled=%")
+ .c(Binder.getCallingUid())
+ .c(enabled).flush();
return mWifiStateMachine.setEnableAutoJoinWhenAssociated(enabled);
}
+ @Override
public boolean getEnableAutoJoinWhenAssociated() {
enforceAccessPermission();
+ mLog.trace("getEnableAutoJoinWhenAssociated uid=%").c(Binder.getCallingUid()).flush();
return mWifiStateMachine.getEnableAutoJoinWhenAssociated();
}
/* Return the Wifi Connection statistics object */
+ @Override
public WifiConnectionStatistics getConnectionStatistics() {
enforceAccessPermission();
enforceReadCredentialPermission();
+ mLog.trace("getConnectionStatistics uid=%").c(Binder.getCallingUid()).flush();
if (mWifiStateMachineChannel != null) {
return mWifiStateMachine.syncGetConnectionStatistics(mWifiStateMachineChannel);
} else {
@@ -1798,16 +2399,18 @@
}
}
+ @Override
public void factoryReset() {
enforceConnectivityInternalPermission();
-
+ mLog.trace("factoryReset uid=%").c(Binder.getCallingUid()).flush();
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) {
return;
}
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
- // Turn mobile hotspot off
- setWifiApEnabled(null, false);
+ // Turn mobile hotspot off - will also clear any registered LOHS requests when it is
+ // shut down
+ stopSoftApInternal();
}
if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) {
@@ -1818,12 +2421,15 @@
/* ignore - local call */
}
// Delete all Wifi SSIDs
- List<WifiConfiguration> networks = getConfiguredNetworks();
- if (networks != null) {
- for (WifiConfiguration config : networks) {
- removeNetwork(config.networkId);
+ if (mWifiStateMachineChannel != null) {
+ List<WifiConfiguration> networks = mWifiStateMachine.syncGetConfiguredNetworks(
+ Binder.getCallingUid(), mWifiStateMachineChannel);
+ if (networks != null) {
+ for (WifiConfiguration config : networks) {
+ removeNetwork(config.networkId);
+ }
+ saveConfiguration();
}
- saveConfiguration();
}
}
}
@@ -1839,11 +2445,6 @@
return validity == null || logAndReturnFalse(validity);
}
- public static boolean isValidPasspoint(WifiConfiguration config) {
- String validity = checkPasspointValidity(config);
- return validity == null || logAndReturnFalse(validity);
- }
-
public static String checkValidity(WifiConfiguration config) {
if (config.allowedKeyManagement == null)
return "allowed kmgmt";
@@ -1860,38 +2461,22 @@
return "not PSK or 8021X";
}
}
- return null;
- }
-
- public static String checkPasspointValidity(WifiConfiguration config) {
- if (!TextUtils.isEmpty(config.FQDN)) {
- /* this is passpoint configuration; it must not have an SSID */
- if (!TextUtils.isEmpty(config.SSID)) {
- return "SSID not expected for Passpoint: '" + config.SSID +
- "' FQDN " + toHexString(config.FQDN);
+ if (config.getIpAssignment() == IpConfiguration.IpAssignment.STATIC) {
+ StaticIpConfiguration staticIpConf = config.getStaticIpConfiguration();
+ if (staticIpConf == null) {
+ return "null StaticIpConfiguration";
}
- /* this is passpoint configuration; it must have a providerFriendlyName */
- if (TextUtils.isEmpty(config.providerFriendlyName)) {
- return "no provider friendly name";
- }
- WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
- /* this is passpoint configuration; it must have enterprise config */
- if (enterpriseConfig == null
- || enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.NONE ) {
- return "no enterprise config";
- }
- if ((enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS ||
- enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TTLS ||
- enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.PEAP) &&
- enterpriseConfig.getCaCertificate() == null) {
- return "no CA certificate";
+ if (staticIpConf.ipAddress == null) {
+ return "null static ip Address";
}
}
return null;
}
+ @Override
public Network getCurrentNetwork() {
enforceAccessPermission();
+ mLog.trace("getCurrentNetwork uid=%").c(Binder.getCallingUid()).flush();
return mWifiStateMachine.getCurrentNetwork();
}
@@ -1907,46 +2492,6 @@
return sb.toString();
}
- /**
- * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION or
- * android.Manifest.permission.ACCESS_FINE_LOCATION and a corresponding app op is allowed
- */
- private boolean checkCallerCanAccessScanResults(String callingPackage, int uid) {
- if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_FINE_LOCATION, uid)
- == PackageManager.PERMISSION_GRANTED
- && checkAppOppAllowed(AppOpsManager.OP_FINE_LOCATION, callingPackage, uid)) {
- return true;
- }
-
- if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_COARSE_LOCATION, uid)
- == PackageManager.PERMISSION_GRANTED
- && checkAppOppAllowed(AppOpsManager.OP_COARSE_LOCATION, callingPackage, uid)) {
- return true;
- }
- boolean apiLevel23App = isMApp(mContext, callingPackage);
- // Pre-M apps running in the foreground should continue getting scan results
- if (!apiLevel23App && isForegroundApp(callingPackage)) {
- return true;
- }
- Log.e(TAG, "Permission denial: Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION "
- + "permission to get scan results");
- return false;
- }
-
- private boolean checkAppOppAllowed(int op, String callingPackage, int uid) {
- return mAppOps.noteOp(op, uid, callingPackage) == AppOpsManager.MODE_ALLOWED;
- }
-
- private static boolean isMApp(Context context, String pkgName) {
- try {
- return context.getPackageManager().getApplicationInfo(pkgName, 0)
- .targetSdkVersion >= Build.VERSION_CODES.M;
- } catch (PackageManager.NameNotFoundException e) {
- // In case of exception, assume M app (more strict checking)
- }
- return true;
- }
-
public void hideCertFromUnaffiliatedUsers(String alias) {
mCertManager.hideCertFromUnaffiliatedUsers(alias);
}
@@ -1956,23 +2501,105 @@
}
/**
- * Return true if the specified package name is a foreground app.
- *
- * @param pkgName application package name.
- */
- private boolean isForegroundApp(String pkgName) {
- ActivityManager am = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
- List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
- return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
- }
-
- /**
* Enable/disable WifiConnectivityManager at runtime
*
* @param enabled true-enable; false-disable
*/
+ @Override
public void enableWifiConnectivityManager(boolean enabled) {
enforceConnectivityInternalPermission();
+ mLog.trace("enableWifiConnectivityManager uid=% enabled=%")
+ .c(Binder.getCallingUid())
+ .c(enabled).flush();
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() {
+ enforceNetworkSettingsPermission();
+ mLog.trace("retrieveBackupData uid=%").c(Binder.getCallingUid()).flush();
+ 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) {
+ enforceNetworkSettingsPermission();
+ mLog.trace("restoreBackupData uid=%").c(Binder.getCallingUid()).flush();
+ 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) {
+ enforceNetworkSettingsPermission();
+ mLog.trace("restoreSupplicantBackupData uid=%").c(Binder.getCallingUid()).flush();
+ 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/WifiSettingsStore.java b/service/java/com/android/server/wifi/WifiSettingsStore.java
index 298e16d..40ae05c 100644
--- a/service/java/com/android/server/wifi/WifiSettingsStore.java
+++ b/service/java/com/android/server/wifi/WifiSettingsStore.java
@@ -224,4 +224,14 @@
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
0) == 1;
}
+
+ /**
+ * Get Location Mode settings for the context
+ * @param context
+ * @return Location Mode setting
+ */
+ public int getLocationModeSetting(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index 6e5e28e..ab7cd01 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;
@@ -42,6 +41,7 @@
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
+import android.net.IpConfiguration;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgent;
@@ -54,9 +54,11 @@
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
+import android.net.TrafficStats;
import android.net.dhcp.DhcpClient;
import android.net.ip.IpManager;
-import android.net.wifi.PasspointManagementObjectDefinition;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IClientInterface;
import android.net.wifi.RssiPacketCountInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.ScanSettings;
@@ -73,6 +75,7 @@
import android.net.wifi.WpsInfo;
import android.net.wifi.WpsResult;
import android.net.wifi.WpsResult.Status;
+import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.p2p.IWifiP2pManager;
import android.os.BatteryStats;
import android.os.Binder;
@@ -85,7 +88,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;
@@ -105,11 +107,20 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.connectivity.KeepalivePacketData;
+import com.android.server.wifi.hotspot2.AnqpEvent;
import com.android.server.wifi.hotspot2.IconEvent;
import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.PasspointManager;
import com.android.server.wifi.hotspot2.Utils;
+import com.android.server.wifi.hotspot2.WnmData;
+import com.android.server.wifi.nano.WifiMetricsProto;
+import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
import com.android.server.wifi.p2p.WifiP2pServiceImpl;
+import com.android.server.wifi.util.NativeUtil;
import com.android.server.wifi.util.TelephonyUtil;
+import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData;
+import com.android.server.wifi.util.TelephonyUtil.SimAuthResponseData;
+import com.android.server.wifi.util.WifiPermissionsUtil;
import java.io.BufferedReader;
import java.io.FileDescriptor;
@@ -121,12 +132,12 @@
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Queue;
-import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -147,22 +158,25 @@
*
* @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 static final String EXTRA_OSU_ICON_QUERY_BSSID = "BSSID";
+ private static final String EXTRA_OSU_ICON_QUERY_FILENAME = "FILENAME";
+
+ private boolean mVerboseLoggingEnabled = false;
+
/* debug flag, indicating if handling of ASSOCIATION_REJECT ended up blacklisting
* the corresponding BSSID.
*/
@@ -173,26 +187,29 @@
*
* @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;
private WifiNative mWifiNative;
+ private WifiPermissionsUtil mWifiPermissionsUtil;
private WifiConfigManager mWifiConfigManager;
private WifiConnectivityManager mWifiConnectivityManager;
- private WifiQualifiedNetworkSelector mWifiQualifiedNetworkSelector;
private INetworkManagementService mNwService;
+ private IClientInterface mClientInterface;
private ConnectivityManager mCm;
- private BaseWifiLogger mWifiLogger;
+ private BaseWifiDiagnostics mWifiDiagnostics;
private WifiApConfigStore mWifiApConfigStore;
private final boolean mP2pSupported;
private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
@@ -202,6 +219,9 @@
private final PropertyService mPropertyService;
private final BuildProperties mBuildProperties;
private final WifiCountryCode mCountryCode;
+ // Object holding most recent wifi score report and bad Linkspeed count
+ private final WifiScoreReport mWifiScoreReport;
+ private final PasspointManager mPasspointManager;
/* Scan results handling */
private List<ScanDetail> mScanResults = new ArrayList<>();
@@ -214,21 +234,22 @@
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;
+ private final StateMachineDeathRecipient mDeathRecipient =
+ new StateMachineDeathRecipient(this, CMD_CLIENT_INTERFACE_BINDER_DEATH);
+ private final WifiNative.VendorHalDeathEventHandler mVendorHalDeathRecipient = () -> {
+ sendMessage(CMD_VENDOR_HAL_HWBINDER_DEATH);
+ };
+ private boolean mIpReachabilityDisconnectEnabled = true;
@Override
public void onRssiThresholdBreached(byte curRssi) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.e(TAG, "onRssiThresholdBreach event. Cur Rssi = " + curRssi);
}
sendMessage(CMD_RSSI_THRESHOLD_BREACH, curRssi);
@@ -248,7 +269,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) +
@@ -272,26 +293,16 @@
private int mOperationalMode = CONNECT_MODE;
private boolean mIsScanOngoing = false;
private boolean mIsFullScanOngoing = false;
- private boolean mSendScanResultsBroadcast = false;
- private final Queue<Message> mBufferedScanMsg = new LinkedList<Message>();
- private WorkSource mScanWorkSource = null;
+ private final Queue<Message> mBufferedScanMsg = new LinkedList<>();
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";
private static final String CUSTOMIZED_SCAN_WORKSOURCE = "customized_scan_worksource";
private static final String SCAN_REQUEST_TIME = "scan_request_time";
- /* Tracks if state machine has received any screen state change broadcast yet.
- * We can miss one of these at boot.
- */
- private AtomicBoolean mScreenBroadcastReceived = new AtomicBoolean(false);
-
private boolean mBluetoothConnectionActive = false;
private PowerManager.WakeLock mSuspendWakeLock;
@@ -318,31 +329,13 @@
*/
private static final int SUPPLICANT_RESTART_TRIES = 5;
+ /**
+ * Value to set in wpa_supplicant "bssid" field when we don't want to restrict connection to
+ * a specific AP.
+ */
+ public static final String SUPPLICANT_BSSID_ANY = "any";
+
private int mSupplicantRestartCount = 0;
- /* Tracks sequence number on stop failure message */
- 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;
-
- /* Tracks sequence number on a driver time out */
- 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.
@@ -369,177 +362,76 @@
private int mWifiLinkLayerStatsSupported = 4; // Temporary disable
- // Whether the state machine goes thru the Disconnecting->Disconnected->ObtainingIpAddress
- private boolean mAutoRoaming = false;
+ // Indicates that framework is attempting to roam, set true on CMD_START_ROAM, set false when
+ // wifi connects or fails to connect
+ private boolean mIsAutoRoaming = false;
// Roaming failure count
private int mRoamFailCount = 0;
- // This is the BSSID we are trying to associate to, it can be set to "any"
+ // This is the BSSID we are trying to associate to, it can be set to SUPPLICANT_BSSID_ANY
// if we havent selected a BSSID for joining.
- // if we havent selected a BSSID for joining.
- // The BSSID we are associated to is found in mWifiInfo
- private String mTargetRoamBSSID = "any";
- //This one is used to track whta is the current target network ID. This is used for error
+ private String mTargetRoamBSSID = SUPPLICANT_BSSID_ANY;
+ // This one is used to track whta is the current target network ID. This is used for error
// handling during connection setup since many error message from supplicant does not report
// SSID Once connected, it will be set to invalid
private int mTargetNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
-
private long mLastDriverRoamAttempt = 0;
-
private WifiConfiguration targetWificonfiguration = null;
- // Used as debug to indicate which configuration last was saved
- private WifiConfiguration lastSavedConfigurationAttempt = null;
-
- // 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;
- }
-
- public void autoRoamSetBSSID(int netId, String bssid) {
- autoRoamSetBSSID(mWifiConfigManager.getWifiConfiguration(netId), bssid);
- }
-
- public boolean autoRoamSetBSSID(WifiConfiguration config, String bssid) {
- boolean ret = true;
- if (mTargetRoamBSSID == null) mTargetRoamBSSID = "any";
- if (bssid == null) bssid = "any";
- if (config == null) return false; // Nothing to do
-
- if (mTargetRoamBSSID != null
- && bssid.equals(mTargetRoamBSSID) && bssid.equals(config.BSSID)) {
- return false; // We didnt change anything
+ /**
+ * Method to clear {@link #mTargetRoamBSSID} and reset the the current connected network's
+ * bssid in wpa_supplicant after a roam/connect attempt.
+ */
+ public boolean clearTargetBssid(String dbg) {
+ WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
+ if (config == null) {
+ return false;
}
- if (!mTargetRoamBSSID.equals("any") && bssid.equals("any")) {
- // Changing to ANY
- if (!mWifiConfigManager.ROAM_ON_ANY) {
- ret = false; // Nothing to do
- }
- }
+ String bssid = SUPPLICANT_BSSID_ANY;
if (config.BSSID != null) {
bssid = config.BSSID;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.d(TAG, "force BSSID to " + bssid + "due to config");
}
}
-
- if (DBG) {
- logd("autoRoamSetBSSID " + bssid + " key=" + config.configKey());
+ if (mVerboseLoggingEnabled) {
+ logd(dbg + " clearTargetBssid " + bssid + " key=" + config.configKey());
}
mTargetRoamBSSID = bssid;
- mWifiConfigManager.saveWifiConfigBSSID(config, bssid);
- return ret;
+ return mWifiNative.setConfiguredNetworkBSSID(bssid);
}
/**
- * set Config's default BSSID (for association purpose)
+ * Set Config's default BSSID (for association purpose) and {@link #mTargetRoamBSSID}
* @param config config need set BSSID
* @param bssid default BSSID to assocaite with when connect to this network
* @return false -- does not change the current default BSSID of the configure
* true -- change the current default BSSID of the configur
*/
private boolean setTargetBssid(WifiConfiguration config, String bssid) {
- if (config == null) {
+ if (config == null || bssid == null) {
return false;
}
-
if (config.BSSID != null) {
bssid = config.BSSID;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
Log.d(TAG, "force BSSID to " + bssid + "due to config");
}
}
-
- if (bssid == null) {
- bssid = "any";
- }
-
- String networkSelectionBSSID = config.getNetworkSelectionStatus()
- .getNetworkSelectionBSSID();
- if (networkSelectionBSSID != null && networkSelectionBSSID.equals(bssid)) {
- if (DBG) {
- Log.d(TAG, "Current preferred BSSID is the same as the target one");
- }
- return false;
- }
-
- if (DBG) {
- Log.d(TAG, "target set to " + config.SSID + ":" + bssid);
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "setTargetBssid set to " + bssid + " key=" + config.configKey());
}
mTargetRoamBSSID = bssid;
- mWifiConfigManager.saveWifiConfigBSSID(config, bssid);
+ config.getNetworkSelectionStatus().setNetworkSelectionBSSID(bssid);
return true;
}
- /**
- * Save the UID correctly depending on if this is a new or existing network.
- * @return true if operation is authorized, false otherwise
- */
- boolean recordUidIfAuthorized(WifiConfiguration config, int uid, boolean onlyAnnotate) {
- if (!mWifiConfigManager.isNetworkConfigured(config)) {
- config.creatorUid = uid;
- config.creatorName = mContext.getPackageManager().getNameForUid(uid);
- } else if (!mWifiConfigManager.canModifyNetwork(uid, config, onlyAnnotate)) {
- return false;
- }
-
- config.lastUpdateUid = uid;
- config.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
-
- return true;
-
- }
-
- /**
- * Checks to see if user has specified if the apps configuration is connectable.
- * If the user hasn't specified we query the user and return true.
- *
- * @param message The message to be deferred
- * @param netId Network id of the configuration to check against
- * @param allowOverride If true we won't defer to the user if the uid of the message holds the
- * CONFIG_OVERRIDE_PERMISSION
- * @return True if we are waiting for user feedback or netId is invalid. False otherwise.
- */
- boolean deferForUserInput(Message message, int netId, boolean allowOverride){
- final WifiConfiguration config = mWifiConfigManager.getWifiConfiguration(netId);
-
- // We can only evaluate saved configurations.
- if (config == null) {
- logd("deferForUserInput: configuration for netId=" + netId + " not stored");
- return true;
- }
-
- switch (config.userApproved) {
- case WifiConfiguration.USER_APPROVED:
- case WifiConfiguration.USER_BANNED:
- return false;
- case WifiConfiguration.USER_PENDING:
- default: // USER_UNSPECIFIED
- /* the intention was to ask user here; but a dialog box is *
- * too invasive; so we are going to allow connection for now */
- config.userApproved = WifiConfiguration.USER_APPROVED;
- return false;
- }
- }
private final IpManager mIpManager;
- private AlarmManager mAlarmManager;
- private PendingIntent mScanIntent;
-
- /* Tracks current frequency mode */
- private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
-
// Channel for sending replies.
private AsyncChannel mReplyChannel = new AsyncChannel();
- private WifiP2pServiceImpl mWifiP2pServiceImpl;
-
// Used to initiate a connection with WifiP2pService
private AsyncChannel mWifiP2pChannel;
@@ -554,8 +446,6 @@
private WifiNetworkAgent mNetworkAgent;
private final Object mWifiReqCountLock = new Object();
- private String[] mWhiteListedSsids = null;
-
private byte[] mRssiRanges;
// Keep track of various statistics, for retrieval by System Apps, i.e. under @SystemApi
@@ -575,16 +465,10 @@
static final int CMD_START_SUPPLICANT = BASE + 11;
/* Stop the supplicant */
static final int CMD_STOP_SUPPLICANT = BASE + 12;
- /* Start the driver */
- static final int CMD_START_DRIVER = BASE + 13;
- /* Stop the driver */
- static final int CMD_STOP_DRIVER = BASE + 14;
/* Indicates Static IP succeeded */
static final int CMD_STATIC_IP_SUCCESS = BASE + 15;
/* Indicates Static IP failed */
static final int CMD_STATIC_IP_FAILURE = BASE + 16;
- /* Indicates supplicant stop failed */
- static final int CMD_STOP_SUPPLICANT_FAILED = BASE + 17;
/* A delayed message sent to start driver when it fail to come up */
static final int CMD_DRIVER_START_TIMED_OUT = BASE + 19;
@@ -600,34 +484,22 @@
static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31;
/* Supplicant commands */
- /* Is supplicant alive ? */
- static final int CMD_PING_SUPPLICANT = BASE + 51;
/* Add/update a network configuration */
static final int CMD_ADD_OR_UPDATE_NETWORK = BASE + 52;
/* Delete a network */
static final int CMD_REMOVE_NETWORK = BASE + 53;
/* Enable a network. The device will attempt a connection to the given network. */
static final int CMD_ENABLE_NETWORK = BASE + 54;
- /* Enable all networks */
- static final int CMD_ENABLE_ALL_NETWORKS = BASE + 55;
- /* Blacklist network. De-prioritizes the given BSSID for connection. */
- static final int CMD_BLACKLIST_NETWORK = BASE + 56;
- /* Clear the blacklist network list */
- static final int CMD_CLEAR_BLACKLIST = BASE + 57;
/* Save configuration */
static final int CMD_SAVE_CONFIG = BASE + 58;
/* Get configured networks */
static final int CMD_GET_CONFIGURED_NETWORKS = BASE + 59;
- /* Get available frequencies */
- static final int CMD_GET_CAPABILITY_FREQ = BASE + 60;
/* Get adaptors */
static final int CMD_GET_SUPPORTED_FEATURES = BASE + 61;
/* Get configured networks with real preSharedKey */
static final int CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS = BASE + 62;
/* Get Link Layer Stats thru HAL */
static final int CMD_GET_LINK_LAYER_STATS = BASE + 63;
- /* Has Carrier configured networks */
- static final int CMD_HAS_CARRIER_CONFIGURED_NETWORKS = BASE + 64;
/* Supplicant commands after driver start*/
/* Initiate a scan */
static final int CMD_START_SCAN = BASE + 71;
@@ -670,12 +542,8 @@
private int testNetworkDisconnectCounter = 0;
- /* Set the frequency band */
- static final int CMD_SET_FREQUENCY_BAND = BASE + 90;
/* Enable TDLS on a specific MAC address */
static final int CMD_ENABLE_TDLS = BASE + 92;
- /* DHCP/IP configuration watchdog */
- static final int CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER = BASE + 93;
/**
* Watchdog for protecting against b/16823537
@@ -708,21 +576,19 @@
static final int CMD_RESET_SIM_NETWORKS = BASE + 101;
/* OSU APIs */
- static final int CMD_ADD_PASSPOINT_MO = BASE + 102;
- static final int CMD_MODIFY_PASSPOINT_MO = BASE + 103;
static final int CMD_QUERY_OSU_ICON = BASE + 104;
/* 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;
+ // Add or update a Passpoint configuration.
+ static final int CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG = BASE + 106;
- int obtainingIpWatchdogCount = 0;
+ // Remove a Passpoint configuration.
+ static final int CMD_REMOVE_PASSPOINT_CONFIG = BASE + 107;
+
+ // Get the list of installed Passpoint configurations.
+ static final int CMD_GET_PASSPOINT_CONFIGS = BASE + 108;
/* Commands from/to the SupplicantStateTracker */
/* Reset the supplicant state tracker */
@@ -731,6 +597,12 @@
int disconnectingWatchdogCount = 0;
static final int DISCONNECTING_GUARD_TIMER_MSEC = 5000;
+ /* Disable p2p watchdog */
+ static final int CMD_DISABLE_P2P_WATCHDOG_TIMER = BASE + 112;
+
+ int mDisableP2pWatchdogCount = 0;
+ static final int DISABLE_P2P_GUARD_TIMER_MSEC = 2000;
+
/* P2p commands */
/* We are ok with no response here since we wont do much with it anyway */
public static final int CMD_ENABLE_P2P = BASE + 131;
@@ -739,7 +611,16 @@
public static final int CMD_DISABLE_P2P_REQ = BASE + 132;
public static final int CMD_DISABLE_P2P_RSP = BASE + 133;
- public static final int CMD_BOOT_COMPLETED = BASE + 134;
+ /**
+ * Indicates the end of boot process, should be used to trigger load from config store,
+ * initiate connection attempt, etc.
+ * */
+ static final int CMD_BOOT_COMPLETED = BASE + 134;
+ /**
+ * Initialize the WifiStateMachine. This is currently used to initialize the
+ * {@link HalDeviceManager} module.
+ */
+ static final int CMD_INITIALIZE = BASE + 135;
/* We now have a valid IP configuration. */
static final int CMD_IP_CONFIGURATION_SUCCESSFUL = BASE + 138;
@@ -754,7 +635,7 @@
/* Reload all networks and reconnect */
static final int CMD_RELOAD_TLS_AND_RECONNECT = BASE + 142;
- static final int CMD_AUTO_CONNECT = BASE + 143;
+ static final int CMD_START_CONNECT = BASE + 143;
private static final int NETWORK_STATUS_UNWANTED_DISCONNECT = 0;
private static final int NETWORK_STATUS_UNWANTED_VALIDATION_FAILED = 1;
@@ -762,9 +643,7 @@
static final int CMD_UNWANTED_NETWORK = BASE + 144;
- static final int CMD_AUTO_ROAM = BASE + 145;
-
- static final int CMD_AUTO_SAVE_NETWORK = BASE + 146;
+ static final int CMD_START_ROAM = BASE + 145;
static final int CMD_ASSOCIATED_BSSID = BASE + 147;
@@ -778,9 +657,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;
@@ -796,9 +672,6 @@
/* used to indicated RSSI threshold breach in hw */
static final int CMD_RSSI_THRESHOLD_BREACH = BASE + 164;
- /* used to indicate that the foreground user was switched */
- static final int CMD_USER_SWITCH = BASE + 165;
-
/* Enable/Disable WifiConnectivityManager */
static final int CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER = BASE + 166;
@@ -820,6 +693,24 @@
/* Enable/disable Neighbor Discovery offload functionality. */
static final int CMD_CONFIG_ND_OFFLOAD = BASE + 204;
+ /* used to indicate that the foreground user was switched */
+ static final int CMD_USER_SWITCH = BASE + 205;
+
+ /* used to indicate that the foreground user was switched */
+ static final int CMD_USER_UNLOCK = BASE + 206;
+
+ /* used to indicate that the foreground user was switched */
+ static final int CMD_USER_STOP = BASE + 207;
+
+ /* Signals that IClientInterface instance underpinning our state is dead. */
+ private static final int CMD_CLIENT_INTERFACE_BINDER_DEATH = BASE + 250;
+
+ /* Signals that the Vendor HAL instance underpinning our state is dead. */
+ private static final int CMD_VENDOR_HAL_HWBINDER_DEATH = BASE + 251;
+
+ /* Indicates that diagnostics should time out a connection start event. */
+ private static final int CMD_DIAGS_CONNECT_TIMEOUT = BASE + 252;
+
// For message logging.
private static final Class[] sMessageClasses = {
AsyncChannel.class, WifiStateMachine.class, DhcpClient.class };
@@ -834,6 +725,8 @@
public static final int SCAN_ONLY_MODE = 2;
/* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */
public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3;
+ /* DISABLED_MODE - Don't connect, don't scan, don't be an AP */
+ public static final int DISABLED_MODE = 4;
private static final int SUCCESS = 1;
private static final int FAILURE = -1;
@@ -853,16 +746,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;
@@ -874,13 +757,17 @@
*/
private long mSupplicantScanIntervalMs;
- /**
- * Minimum time interval between enabling all networks.
- * A device can end up repeatedly connecting to a bad network on screen on/off toggle
- * due to enabling every time. We add a threshold to avoid this.
- */
- private static final int MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS = 10 * 60 * 1000; /* 10 minutes */
- private long mLastEnableAllNetworksTime;
+ private boolean mEnableAutoJoinWhenAssociated;
+ private int mAlwaysEnableScansWhileAssociated;
+ private final int mThresholdQualifiedRssi24;
+ private final int mThresholdQualifiedRssi5;
+ private final int mThresholdSaturatedRssi24;
+ private final int mThresholdSaturatedRssi5;
+ private final int mThresholdMinimumRssi5;
+ private final int mThresholdMinimumRssi24;
+ private final boolean mEnableLinkDebouncing;
+ private final boolean mEnableChipWakeUpWhenAssociated;
+ private final boolean mEnableRssiPollWhenAssociated;
int mRunningBeaconCount = 0;
@@ -894,19 +781,11 @@
private State mSupplicantStartedState = new SupplicantStartedState();
/* Waiting for supplicant to stop and monitor to exit */
private State mSupplicantStoppingState = new SupplicantStoppingState();
- /* Driver start issued, waiting for completed event */
- private State mDriverStartingState = new DriverStartingState();
- /* Driver started */
- private State mDriverStartedState = new DriverStartedState();
/* Wait until p2p is disabled
* This is a special state which is entered right after we exit out of DriverStartedState
* before transitioning to another state.
*/
private State mWaitForP2pDisableState = new WaitForP2pDisableState();
- /* Driver stopping */
- private State mDriverStoppingState = new DriverStoppingState();
- /* Driver stopped */
- private State mDriverStoppedState = new DriverStoppedState();
/* Scan for networks, no connection will be established */
private State mScanModeState = new ScanModeState();
/* Connecting to an access point */
@@ -928,15 +807,6 @@
/* Soft ap state */
private State mSoftApState = new SoftApState();
- public static class SimAuthRequestData {
- int networkId;
- int protocol;
- String ssid;
- // EAP-SIM: data[] contains the 3 rand, one for each of the 3 challenges
- // EAP-AKA/AKA': data[] contains rand & authn couple for the single challenge
- String[] data;
- }
-
/**
* One of {@link WifiManager#WIFI_STATE_DISABLED},
* {@link WifiManager#WIFI_STATE_DISABLING},
@@ -955,8 +825,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,6 +850,14 @@
*/
private final WorkSource mLastRunningWifiUids = new WorkSource();
+ private TelephonyManager mTelephonyManager;
+ private TelephonyManager getTelephonyManager() {
+ if (mTelephonyManager == null) {
+ mTelephonyManager = mWifiInjector.makeTelephonyManager();
+ }
+ return mTelephonyManager;
+ }
+
private final IBatteryStats mBatteryStats;
private final String mTcpBufferSizes;
@@ -989,70 +865,52 @@
// 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 WifiStateTracker mWifiStateTracker;
private final BackupManagerProxy mBackupManagerProxy;
- private int mSystemUiUid = -1;
-
public WifiStateMachine(Context context, FrameworkFacade facade, Looper looper,
UserManager userManager, WifiInjector wifiInjector,
- BackupManagerProxy backupManagerProxy,
- WifiCountryCode countryCode) {
+ BackupManagerProxy backupManagerProxy, WifiCountryCode countryCode,
+ WifiNative wifiNative) {
super("WifiStateMachine", looper);
mWifiInjector = wifiInjector;
mWifiMetrics = mWifiInjector.getWifiMetrics();
- mWifiLastResortWatchdog = wifiInjector.getWifiLastResortWatchdog();
mClock = wifiInjector.getClock();
mPropertyService = wifiInjector.getPropertyService();
mBuildProperties = wifiInjector.getBuildProperties();
mContext = context;
mFacade = facade;
- mWifiNative = WifiNative.getWlanNativeInterface();
+ mWifiNative = wifiNative;
mBackupManagerProxy = backupManagerProxy;
// TODO refactor WifiNative use of context out into it's own class
- mWifiNative.initContext(mContext);
mInterfaceName = mWifiNative.getInterfaceName();
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
mBatteryStats = IBatteryStats.Stub.asInterface(mFacade.getService(
BatteryStats.SERVICE_NAME));
-
+ mWifiStateTracker = wifiInjector.getWifiStateTracker();
IBinder b = mFacade.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNwService = INetworkManagementService.Stub.asInterface(b);
mP2pSupported = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_DIRECT);
- mWifiConfigManager = mFacade.makeWifiConfigManager(context, mWifiNative, facade,
- mWifiInjector.getClock(), userManager, mWifiInjector.getKeyStore());
- mWifiMonitor = WifiMonitor.getInstance();
+ mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil();
+ mWifiConfigManager = mWifiInjector.getWifiConfigManager();
+ mWifiApConfigStore = mWifiInjector.getWifiApConfigStore();
- boolean enableFirmwareLogs = mContext.getResources().getBoolean(
- R.bool.config_wifi_enable_wifi_firmware_debugging);
+ mPasspointManager = mWifiInjector.getPasspointManager();
- if (enableFirmwareLogs) {
- mWifiLogger = facade.makeRealLogger(mContext, this, mWifiNative, mBuildProperties);
- } else {
- mWifiLogger = facade.makeBaseLogger();
- }
+ mWifiMonitor = mWifiInjector.getWifiMonitor();
+ mWifiDiagnostics = mWifiInjector.makeWifiDiagnostics(mWifiNative);
mWifiInfo = new WifiInfo();
- mWifiQualifiedNetworkSelector = new WifiQualifiedNetworkSelector(mWifiConfigManager,
- mContext, mWifiInfo, mWifiInjector.getClock());
- mSupplicantStateTracker = mFacade.makeSupplicantStateTracker(
- context, mWifiConfigManager, getHandler());
+ mSupplicantStateTracker =
+ mFacade.makeSupplicantStateTracker(context, mWifiConfigManager, getHandler());
mLinkProperties = new LinkProperties();
- IBinder s1 = mFacade.getService(Context.WIFI_P2P_SERVICE);
- mWifiP2pServiceImpl = (WifiP2pServiceImpl) IWifiP2pManager.Stub.asInterface(s1);
-
mNetworkInfo.setIsAvailable(false);
mLastBssid = null;
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
@@ -1061,27 +919,20 @@
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);
mCountryCode = countryCode;
+ mWifiScoreReport = new WifiScoreReport(mContext, mWifiConfigManager);
+
mUserWantsSuspendOpt.set(mFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED, 1) == 1);
@@ -1139,15 +990,33 @@
mTcpBufferSizes = mContext.getResources().getString(
com.android.internal.R.string.config_wifi_tcp_buffers);
+ // Load Device configs
+ mEnableAutoJoinWhenAssociated = context.getResources().getBoolean(
+ R.bool.config_wifi_framework_enable_associated_network_selection);
+ mThresholdQualifiedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
+ mThresholdQualifiedRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
+ mThresholdSaturatedRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
+ mThresholdSaturatedRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
+ mThresholdMinimumRssi5 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
+ mThresholdMinimumRssi24 = context.getResources().getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
+ mEnableLinkDebouncing = mContext.getResources().getBoolean(
+ R.bool.config_wifi_enable_disconnection_debounce);
+ mEnableChipWakeUpWhenAssociated = true;
+ mEnableRssiPollWhenAssociated = true;
+
// CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mInitialState, mDefaultState);
addState(mSupplicantStartingState, mDefaultState);
addState(mSupplicantStartedState, mDefaultState);
- addState(mDriverStartingState, mSupplicantStartedState);
- addState(mDriverStartedState, mSupplicantStartedState);
- addState(mScanModeState, mDriverStartedState);
- addState(mConnectModeState, mDriverStartedState);
+ addState(mScanModeState, mSupplicantStartedState);
+ addState(mConnectModeState, mSupplicantStartedState);
addState(mL2ConnectedState, mConnectModeState);
addState(mObtainingIpState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
@@ -1156,8 +1025,6 @@
addState(mDisconnectedState, mConnectModeState);
addState(mWpsRunningState, mConnectModeState);
addState(mWaitForP2pDisableState, mSupplicantStartedState);
- addState(mDriverStoppingState, mSupplicantStartedState);
- addState(mDriverStoppedState, mSupplicantStartedState);
addState(mSupplicantStoppingState, mDefaultState);
addState(mSoftApState, mDefaultState);
// CHECKSTYLE:ON IndentationCheck
@@ -1170,6 +1037,10 @@
//start the state machine
start();
+ // Learn the initial state of whether the screen is on.
+ // We update this field when we receive broadcasts from the system.
+ handleScreenStateChanged(powerManager.isInteractive());
+
mWifiMonitor.registerHandler(mInterfaceName, CMD_TARGET_BSSID, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, CMD_ASSOCIATED_BSSID, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ANQP_DONE_EVENT, getHandler());
@@ -1177,7 +1048,6 @@
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.AUTHENTICATION_FAILURE_EVENT,
getHandler());
- mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.DRIVER_HUNG_EVENT, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.GAS_QUERY_DONE_EVENT, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.GAS_QUERY_START_EVENT,
getHandler());
@@ -1191,8 +1061,6 @@
getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SCAN_FAILED_EVENT, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SCAN_RESULTS_EVENT, getHandler());
- mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SSID_REENABLED, getHandler());
- mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SSID_TEMP_DISABLED, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUP_CONNECTION_EVENT, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUP_DISCONNECTION_EVENT,
getHandler());
@@ -1205,21 +1073,25 @@
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.WPS_SUCCESS_EVENT, getHandler());
mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.WPS_TIMEOUT_EVENT, getHandler());
+ mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.ASSOCIATION_REJECTION_EVENT,
+ mWifiMetrics.getHandler());
+ mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.AUTHENTICATION_FAILURE_EVENT,
+ mWifiMetrics.getHandler());
+ mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_CONNECTION_EVENT,
+ mWifiMetrics.getHandler());
+ mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.NETWORK_DISCONNECTION_EVENT,
+ mWifiMetrics.getHandler());
+ mWifiMonitor.registerHandler(mInterfaceName, WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT,
+ mWifiMetrics.getHandler());
+ mWifiMonitor.registerHandler(mInterfaceName, CMD_ASSOCIATED_BSSID,
+ mWifiMetrics.getHandler());
+ mWifiMonitor.registerHandler(mInterfaceName, CMD_TARGET_BSSID,
+ mWifiMetrics.getHandler());
+
final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-
- try {
- mSystemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui",
- PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
- } 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 {
@@ -1239,20 +1111,22 @@
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);
}
}
@Override
public void onProvisioningSuccess(LinkProperties newLp) {
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL);
sendMessage(CMD_UPDATE_LINKPROPERTIES, newLp);
sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
}
@Override
public void onProvisioningFailure(LinkProperties newLp) {
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST);
sendMessage(CMD_IP_CONFIGURATION_LOST);
}
@@ -1263,6 +1137,7 @@
@Override
public void onReachabilityLost(String logMsg) {
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_IP_REACHABILITY_LOST);
sendMessage(CMD_IP_REACHABILITY_LOST, logMsg);
}
@@ -1295,54 +1170,36 @@
return mFacade.getBroadcast(mContext, requestCode, intent, 0);
}
- int getVerboseLoggingLevel() {
- return mVerboseLoggingLevel;
- }
-
- void enableVerboseLogging(int verbose) {
- if (mVerboseLoggingLevel == verbose) {
- // We are already at the desired verbosity, avoid resetting StateMachine log records by
- // returning here until underlying bug is fixed (b/28027593)
- return;
- }
- 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) {
- mWifiNative.setSupplicantLogLevel("DEBUG");
- } else {
- mWifiNative.setSupplicantLogLevel("INFO");
- }
+ mWifiNative.setSupplicantLogLevel(mVerboseLoggingEnabled);
}
- void updateLoggingLevel() {
- if (mVerboseLoggingLevel > 0) {
- DBG = true;
+ /**
+ * Method to update logging level in wifi service related classes.
+ *
+ * @param verbose int logging level to use
+ */
+ public 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);
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.enableVerboseLogging(mVerboseLoggingLevel);
- }
+ mCountryCode.enableVerboseLogging(verbose);
+ mWifiScoreReport.enableVerboseLogging(mVerboseLoggingEnabled);
+ mWifiDiagnostics.startLogging(mVerboseLoggingEnabled);
+ mWifiMonitor.enableVerboseLogging(verbose);
+ mWifiNative.enableVerboseLogging(verbose);
+ mWifiConfigManager.enableVerboseLogging(verbose);
+ mSupplicantStateTracker.enableVerboseLogging(verbose);
}
private static final String SYSTEM_PROPERTY_LOG_CONTROL_WIFIHAL = "log.tag.WifiHAL";
@@ -1356,15 +1213,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() {
@@ -1376,15 +1224,16 @@
}
public void clearANQPCache() {
- mWifiConfigManager.trimANQPCache(true);
+ // TODO(b/31065385)
+ // mWifiConfigManager.trimANQPCache(true);
}
public void setAllowScansWithTraffic(int enabled) {
- mWifiConfigManager.mAlwaysEnableScansWhileAssociated.set(enabled);
+ mAlwaysEnableScansWhileAssociated = enabled;
}
public int getAllowScansWithTraffic() {
- return mWifiConfigManager.mAlwaysEnableScansWhileAssociated.get();
+ return mAlwaysEnableScansWhileAssociated;
}
/*
@@ -1396,7 +1245,7 @@
}
public boolean getEnableAutoJoinWhenAssociated() {
- return mWifiConfigManager.getEnableAutoJoinWhenAssociated();
+ return mEnableAutoJoinWhenAssociated;
}
private boolean setRandomMacOui() {
@@ -1415,6 +1264,56 @@
}
/**
+ * Helper method to lookup the framework network ID of the network currently configured in
+ * wpa_supplicant using the provided supplicant network ID. This is needed for translating the
+ * networkID received from all {@link WifiMonitor} events.
+ *
+ * @param supplicantNetworkId Network ID of network in wpa_supplicant.
+ * @return Corresponding Internal configured network ID
+ * TODO(b/31080843): This is ugly! We need to hide this translation of networkId's. This will
+ * be handled once we move all of this connection logic to wificond.
+ */
+ private int lookupFrameworkNetworkId(int supplicantNetworkId) {
+ return mWifiNative.getFrameworkNetworkId(supplicantNetworkId);
+ }
+
+ /**
+ * Initiates connection to a network specified by the user/app. This method checks if the
+ * requesting app holds the WIFI_CONFIG_OVERRIDE permission.
+ *
+ * @param netId Id network to initiate connection.
+ * @param uid UID of the app requesting the connection.
+ * @param forceReconnect Whether to force a connection even if we're connected to the same
+ * network currently.
+ */
+ private boolean connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) {
+ logd("connectToUserSelectNetwork netId " + netId + ", uid " + uid
+ + ", forceReconnect = " + forceReconnect);
+ if (mWifiConfigManager.getConfiguredNetwork(netId) == null) {
+ loge("connectToUserSelectNetwork Invalid network Id=" + netId);
+ return false;
+ }
+ if (!mWifiConfigManager.enableNetwork(netId, true, uid)
+ || !mWifiConfigManager.checkAndUpdateLastConnectUid(netId, uid)) {
+ logi("connectToUserSelectNetwork Allowing uid " + uid
+ + " with insufficient permissions to connect=" + netId);
+ } else {
+ // Note user connect choice here, so that it will be considered in the next network
+ // selection.
+ mWifiConnectivityManager.setUserConnectChoice(netId);
+ }
+ if (!forceReconnect && mWifiInfo.getNetworkId() == netId) {
+ // We're already connected to the user specified network, don't trigger a
+ // reconnection unless it was forced.
+ logi("connectToUserSelectNetwork already connecting/connected=" + netId);
+ } else {
+ mWifiConnectivityManager.prepareForForcedConnection(netId);
+ startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY);
+ }
+ return true;
+ }
+
+ /**
* ******************************************************
* Methods exposed for public use
* ******************************************************
@@ -1425,16 +1324,6 @@
}
/**
- * TODO: doc
- */
- public boolean syncPingSupplicant(AsyncChannel channel) {
- Message resultMsg = channel.sendMessageSynchronously(CMD_PING_SUPPLICANT);
- boolean result = (resultMsg.arg1 != FAILURE);
- resultMsg.recycle();
- return result;
- }
-
- /**
* Initiate a wifi scan. If workSource is not null, blame is given to it, otherwise blame is
* given to callingUid.
*
@@ -1448,47 +1337,16 @@
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);
}
- // called from BroadcastListener
-
- /**
- * Start reading new scan data
- * Data comes in as:
- * "scancount=5\n"
- * "nextcount=5\n"
- * "apcount=3\n"
- * "trunc\n" (optional)
- * "bssid=...\n"
- * "ssid=...\n"
- * "freq=...\n" (in Mhz)
- * "level=...\n"
- * "dist=...\n" (in cm)
- * "distsd=...\n" (standard deviation, in cm)
- * "===="
- * "bssid=...\n"
- * etc
- * "===="
- * "bssid=...\n"
- * etc
- * "%%%%"
- * "apcount=2\n"
- * "bssid=...\n"
- * etc
- * "%%%%
- * etc
- * "----"
- */
- private final static boolean DEBUG_PARSE = false;
-
private long mDisconnectedTimeStamp = 0;
public long getDisconnectedTimeMilli() {
if (getCurrentState() == mDisconnectedState
&& mDisconnectedTimeStamp != 0) {
- long now_ms = System.currentTimeMillis();
+ long now_ms = mClock.getWallClockMillis();
return now_ms - mDisconnectedTimeStamp;
}
return 0;
@@ -1501,23 +1359,23 @@
// For debugging, keep track of last message status handling
// TODO, find an equivalent mechanism as part of parent class
- private static int MESSAGE_HANDLING_STATUS_PROCESSED = 2;
- private static int MESSAGE_HANDLING_STATUS_OK = 1;
- private static int MESSAGE_HANDLING_STATUS_UNKNOWN = 0;
- private static int MESSAGE_HANDLING_STATUS_REFUSED = -1;
- private static int MESSAGE_HANDLING_STATUS_FAIL = -2;
- private static int MESSAGE_HANDLING_STATUS_OBSOLETE = -3;
- private static int MESSAGE_HANDLING_STATUS_DEFERRED = -4;
- private static int MESSAGE_HANDLING_STATUS_DISCARD = -5;
- private static int MESSAGE_HANDLING_STATUS_LOOPED = -6;
- private static int MESSAGE_HANDLING_STATUS_HANDLING_ERROR = -7;
+ private static final int MESSAGE_HANDLING_STATUS_PROCESSED = 2;
+ private static final int MESSAGE_HANDLING_STATUS_OK = 1;
+ private static final int MESSAGE_HANDLING_STATUS_UNKNOWN = 0;
+ private static final int MESSAGE_HANDLING_STATUS_REFUSED = -1;
+ private static final int MESSAGE_HANDLING_STATUS_FAIL = -2;
+ private static final int MESSAGE_HANDLING_STATUS_OBSOLETE = -3;
+ private static final int MESSAGE_HANDLING_STATUS_DEFERRED = -4;
+ private static final int MESSAGE_HANDLING_STATUS_DISCARD = -5;
+ private static final int MESSAGE_HANDLING_STATUS_LOOPED = -6;
+ private static final int MESSAGE_HANDLING_STATUS_HANDLING_ERROR = -7;
private int messageHandlingStatus = 0;
//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));
@@ -1540,7 +1398,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;
@@ -1559,7 +1417,7 @@
return sb.toString();
}
- WifiLinkLayerStats getWifiLinkLayerStats(boolean dbg) {
+ WifiLinkLayerStats getWifiLinkLayerStats() {
WifiLinkLayerStats stats = null;
if (mWifiLinkLayerStatsSupported > 0) {
String name = "wlan0";
@@ -1567,7 +1425,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;
@@ -1579,7 +1437,7 @@
long mRxPkts = mFacade.getRxPackets(mInterfaceName);
mWifiInfo.updatePacketRates(mTxPkts, mRxPkts);
} else {
- mWifiInfo.updatePacketRates(stats);
+ mWifiInfo.updatePacketRates(stats, lastLinkLayerStatsUpdate);
}
return stats;
}
@@ -1627,26 +1485,22 @@
Set<Integer> freqs = null;
if (settings != null && settings.channelSet != null) {
- freqs = new HashSet<Integer>();
+ freqs = new HashSet<>();
for (WifiChannel channel : settings.channelSet) {
freqs.add(channel.freqMHz);
}
}
- // Retrieve the list of hidden networkId's to scan for.
- Set<Integer> hiddenNetworkIds = mWifiConfigManager.getHiddenConfiguredNetworkIds();
+ // Retrieve the list of hidden network SSIDs to scan for.
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks =
+ mWifiConfigManager.retrieveHiddenNetworkList();
// call wifi native to start the scan
- if (startScanNative(freqs, hiddenNetworkIds, workSource)) {
+ if (startScanNative(freqs, hiddenNetworks, workSource)) {
// a full scan covers everything, clearing scan request buffer
if (freqs == null)
mBufferedScanMsg.clear();
messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
- if (workSource != null) {
- // External worksource was passed along the scan request,
- // hence always send a broadcast
- mSendScanResultsBroadcast = true;
- }
return;
}
@@ -1691,7 +1545,8 @@
/**
* return true iff scan request is accepted
*/
- private boolean startScanNative(final Set<Integer> freqs, Set<Integer> hiddenNetworkIds,
+ private boolean startScanNative(final Set<Integer> freqs,
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList,
WorkSource workSource) {
WifiScanner.ScanSettings settings = new WifiScanner.ScanSettings();
if (freqs == null) {
@@ -1706,25 +1561,28 @@
}
settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
- if (hiddenNetworkIds != null && hiddenNetworkIds.size() > 0) {
- int i = 0;
- settings.hiddenNetworkIds = new int[hiddenNetworkIds.size()];
- for (Integer netId : hiddenNetworkIds) {
- settings.hiddenNetworkIds[i++] = netId;
- }
- }
+
+ settings.hiddenNetworks =
+ hiddenNetworkList.toArray(
+ new WifiScanner.ScanSettings.HiddenNetwork[hiddenNetworkList.size()]);
+
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) {
}
};
@@ -1749,7 +1607,7 @@
/**
* TODO: doc
*/
- public void setHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
+ public void setHostApRunning(SoftApModeConfiguration wifiConfig, boolean enable) {
if (enable) {
sendMessage(CMD_START_AP, wifiConfig);
} else {
@@ -1828,19 +1686,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);
}
}
@@ -1848,7 +1706,7 @@
}
public boolean isLinkDebouncing() {
- return linkDebouncing;
+ return mIsLinkDebouncing;
}
/**
@@ -1873,19 +1731,8 @@
/**
* TODO: doc
*/
- public void setDriverStart(boolean enable) {
- if (enable) {
- sendMessage(CMD_START_DRIVER);
- } else {
- sendMessage(CMD_STOP_DRIVER);
- }
- }
-
- /**
- * 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);
}
@@ -1902,7 +1749,7 @@
*/
public List<ScanResult> syncGetScanResultsList() {
synchronized (mScanResultsLock) {
- List<ScanResult> scanList = new ArrayList<ScanResult>();
+ List<ScanResult> scanList = new ArrayList<>();
for (ScanDetail result : mScanResults) {
scanList.add(new ScanResult(result.getScanResult()));
}
@@ -1910,30 +1757,10 @@
}
}
- public int syncAddPasspointManagementObject(AsyncChannel channel, String managementObject) {
- Message resultMsg =
- channel.sendMessageSynchronously(CMD_ADD_PASSPOINT_MO, managementObject);
- int result = resultMsg.arg1;
- resultMsg.recycle();
- return result;
- }
-
- public int syncModifyPasspointManagementObject(AsyncChannel channel, String fqdn,
- List<PasspointManagementObjectDefinition>
- managementObjectDefinitions) {
- Bundle bundle = new Bundle();
- bundle.putString("FQDN", fqdn);
- bundle.putParcelableList("MOS", managementObjectDefinitions);
- Message resultMsg = channel.sendMessageSynchronously(CMD_MODIFY_PASSPOINT_MO, bundle);
- int result = resultMsg.arg1;
- resultMsg.recycle();
- return result;
- }
-
public boolean syncQueryPasspointIcon(AsyncChannel channel, long bssid, String fileName) {
Bundle bundle = new Bundle();
- bundle.putLong("BSSID", bssid);
- bundle.putString("FILENAME", fileName);
+ bundle.putLong(EXTRA_OSU_ICON_QUERY_BSSID, bssid);
+ bundle.putString(EXTRA_OSU_ICON_QUERY_FILENAME, fileName);
Message resultMsg = channel.sendMessageSynchronously(CMD_QUERY_OSU_ICON, bundle);
int result = resultMsg.arg1;
resultMsg.recycle();
@@ -2025,21 +1852,6 @@
}
}
- /**
- * Check if Carrier networks have been configured synchronously
- *
- * @param channel
- * @return
- */
- public boolean syncHasCarrierConfiguredNetworks(
- int uuid, AsyncChannel channel) {
- Message resultMsg = channel.sendMessageSynchronously(
- CMD_HAS_CARRIER_CONFIGURED_NETWORKS, uuid);
- boolean result = resultMsg.obj != null && (boolean) resultMsg.obj;
- resultMsg.recycle();
- return result;
- }
-
public List<WifiConfiguration> syncGetPrivilegedConfiguredNetwork(AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(
CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS);
@@ -2050,7 +1862,53 @@
public WifiConfiguration syncGetMatchingWifiConfig(ScanResult scanResult, AsyncChannel channel) {
Message resultMsg = channel.sendMessageSynchronously(CMD_GET_MATCHING_CONFIG, scanResult);
- return (WifiConfiguration) resultMsg.obj;
+ WifiConfiguration config = (WifiConfiguration) resultMsg.obj;
+ resultMsg.recycle();
+ return config;
+ }
+
+ /**
+ * Add or update a Passpoint configuration synchronously.
+ *
+ * @param channel Channel for communicating with the state machine
+ * @param config The configuration to add or update
+ * @return true on success
+ */
+ public boolean syncAddOrUpdatePasspointConfig(AsyncChannel channel,
+ PasspointConfiguration config, int uid) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG,
+ uid, 0, config);
+ boolean result = (resultMsg.arg1 == SUCCESS);
+ resultMsg.recycle();
+ return result;
+ }
+
+ /**
+ * Remove a Passpoint configuration synchronously.
+ *
+ * @param channel Channel for communicating with the state machine
+ * @param fqdn The FQDN of the Passpoint configuration to remove
+ * @return true on success
+ */
+ public boolean syncRemovePasspointConfig(AsyncChannel channel, String fqdn) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_REMOVE_PASSPOINT_CONFIG,
+ fqdn);
+ boolean result = (resultMsg.arg1 == SUCCESS);
+ resultMsg.recycle();
+ return result;
+ }
+
+ /**
+ * Get the list of installed Passpoint configurations synchronously.
+ *
+ * @param channel Channel for communicating with the state machine
+ * @return List of {@link PasspointConfiguration}
+ */
+ public List<PasspointConfiguration> syncGetPasspointConfigs(AsyncChannel channel) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_GET_PASSPOINT_CONFIGS);
+ List<PasspointConfiguration> result = (List<PasspointConfiguration>) resultMsg.obj;
+ resultMsg.recycle();
+ return result;
}
/**
@@ -2131,7 +1989,7 @@
*/
public boolean syncDisableNetwork(AsyncChannel channel, int netId) {
Message resultMsg = channel.sendMessageSynchronously(WifiManager.DISABLE_NETWORK, netId);
- boolean result = (resultMsg.arg1 != WifiManager.DISABLE_NETWORK_FAILED);
+ boolean result = (resultMsg.what != WifiManager.DISABLE_NETWORK_FAILED);
resultMsg.recycle();
return result;
}
@@ -2141,35 +1999,14 @@
*
* @return a hex string representation of the WPS-NFC configuration token
*/
- public String syncGetWpsNfcConfigurationToken(int netId) {
- return mWifiNative.getNfcWpsConfigurationToken(netId);
- }
-
- /**
- * Blacklist a BSSID. This will avoid the AP if there are
- * alternate APs to connect
- *
- * @param bssid BSSID of the network
- */
- public void addToBlacklist(String bssid) {
- sendMessage(CMD_BLACKLIST_NETWORK, bssid);
- }
-
- /**
- * Clear the blacklist list
- */
- public void clearBlacklist() {
- sendMessage(CMD_CLEAR_BLACKLIST);
+ public String syncGetCurrentNetworkWpsNfcConfigurationToken() {
+ return mWifiNative.getCurrentNetworkWpsNfcConfigurationToken();
}
public void enableRssiPolling(boolean enabled) {
sendMessage(CMD_ENABLE_RSSI_POLL, enabled ? 1 : 0, 0);
}
- public void enableAllNetworks() {
- sendMessage(CMD_ENABLE_ALL_NETWORKS);
- }
-
/**
* Start filtering Multicast v4 packets
*/
@@ -2215,22 +2052,6 @@
}
}
-
- /**
- * Set the operational frequency band
- *
- * @param band
- * @param persist {@code true} if the setting should be remembered.
- */
- public void setFrequencyBand(int band, boolean persist) {
- if (persist) {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.WIFI_FREQUENCY_BAND,
- band);
- }
- sendMessage(CMD_SET_FREQUENCY_BAND, band, 0);
- }
-
/**
* Enable TDLS for a specific MAC address
*/
@@ -2240,20 +2061,6 @@
}
/**
- * Returns the operational frequency band
- */
- public int getFrequencyBand() {
- return mFrequencyBand.get();
- }
-
- /**
- * Returns the wifi configuration file
- */
- public String getConfigFile() {
- return mWifiConfigManager.getConfigFile();
- }
-
- /**
* Send a message indicating bluetooth adapter connection state changed
*/
public void sendBluetoothAdapterStateChange(int state) {
@@ -2333,13 +2140,6 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if ((args != null) && args.length > 1 && WifiMetrics.PROTO_DUMP_ARG.equals(args[0])
- && WifiMetrics.CLEAN_DUMP_ARG.equals(args[1])) {
- // Dump only wifi metrics serialized proto bytes (base64)
- updateWifiMetrics();
- mWifiMetrics.dump(fd, pw, args);
- return;
- }
super.dump(fd, pw, args);
mSupplicantStateTracker.dump(fd, pw, args);
pw.println("mLinkProperties " + mLinkProperties);
@@ -2352,7 +2152,6 @@
pw.println("mOperationalMode " + mOperationalMode);
pw.println("mUserWantsSuspendOpt " + mUserWantsSuspendOpt);
pw.println("mSuspendOptNeedsDisabled " + mSuspendOptNeedsDisabled);
- pw.println("Supplicant status " + mWifiNative.status(true));
if (mCountryCode.getCountryCodeSentToDriver() != null) {
pw.println("CountryCode sent to driver " + mCountryCode.getCountryCodeSentToDriver());
} else {
@@ -2363,14 +2162,6 @@
pw.println("CountryCode was 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 {
@@ -2384,18 +2175,18 @@
}
pw.println("Wlan Wake Reasons:" + mWifiNative.getWlanWakeReasonCount());
pw.println();
- updateWifiMetrics();
- mWifiMetrics.dump(fd, pw, args);
- pw.println();
mWifiConfigManager.dump(fd, pw, args);
pw.println();
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_USER_ACTION);
- mWifiLogger.dump(fd, pw, args);
- mWifiQualifiedNetworkSelector.dump(fd, pw, args);
+ mPasspointManager.dump(pw);
+ pw.println();
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_USER_ACTION);
+ mWifiDiagnostics.dump(fd, pw, args);
dumpIpManager(fd, pw, args);
if (mWifiConnectivityManager != null) {
mWifiConnectivityManager.dump(fd, pw, args);
+ } else {
+ pw.println("mWifiConnectivityManager is not initialized");
}
}
@@ -2403,6 +2194,14 @@
sendMessage(CMD_USER_SWITCH, userId);
}
+ public void handleUserUnlock(int userId) {
+ sendMessage(CMD_USER_UNLOCK, userId);
+ }
+
+ public void handleUserStop(int userId) {
+ sendMessage(CMD_USER_STOP, userId);
+ }
+
/**
* ******************************************************
* Internal private functions
@@ -2411,19 +2210,19 @@
private void logStateAndMessage(Message message, State state) {
messageHandlingStatus = 0;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd(" " + state.getClass().getSimpleName() + " " + getLogRecString(message));
}
}
- /**
- * helper, prints the milli time since boot wi and w/o suspended time
- */
- String printTime() {
- StringBuilder sb = new StringBuilder();
- sb.append(" rt=").append(SystemClock.uptimeMillis());
- sb.append("/").append(SystemClock.elapsedRealtime());
- return sb.toString();
+ @Override
+ protected boolean recordLogRec(Message msg) {
+ switch (msg.what) {
+ case CMD_RSSI_POLL:
+ return mVerboseLoggingEnabled;
+ default:
+ return true;
+ }
}
/**
@@ -2432,6 +2231,7 @@
* @param msg that was processed
* @return information to be logged as a String
*/
+ @Override
protected String getLogRecString(Message msg) {
WifiConfiguration config;
Long now;
@@ -2448,24 +2248,11 @@
if (msg.sendingUid > 0 && msg.sendingUid != Process.WIFI_UID) {
sb.append(" uid=" + msg.sendingUid);
}
- sb.append(" ").append(printTime());
+ sb.append(" rt=").append(mClock.getUptimeSinceBootMillis());
+ sb.append("/").append(mClock.getElapsedSinceBootMillis());
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(" ");
@@ -2511,29 +2298,29 @@
}
break;
case WifiManager.SAVE_NETWORK:
- case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
- if (lastSavedConfigurationAttempt != null) {
- sb.append(" ").append(lastSavedConfigurationAttempt.configKey());
- sb.append(" nid=").append(lastSavedConfigurationAttempt.networkId);
- if (lastSavedConfigurationAttempt.hiddenSSID) {
+ config = (WifiConfiguration) msg.obj;
+ if (config != null) {
+ sb.append(" ").append(config.configKey());
+ sb.append(" nid=").append(config.networkId);
+ if (config.hiddenSSID) {
sb.append(" hidden");
}
- if (lastSavedConfigurationAttempt.preSharedKey != null
- && !lastSavedConfigurationAttempt.preSharedKey.equals("*")) {
+ if (config.preSharedKey != null
+ && !config.preSharedKey.equals("*")) {
sb.append(" hasPSK");
}
- if (lastSavedConfigurationAttempt.ephemeral) {
+ if (config.ephemeral) {
sb.append(" ephemeral");
}
- if (lastSavedConfigurationAttempt.selfAdded) {
+ if (config.selfAdded) {
sb.append(" selfAdded");
}
- sb.append(" cuid=").append(lastSavedConfigurationAttempt.creatorUid);
- sb.append(" suid=").append(lastSavedConfigurationAttempt.lastUpdateUid);
+ sb.append(" cuid=").append(config.creatorUid);
+ sb.append(" suid=").append(config.lastUpdateUid);
}
break;
case WifiManager.FORGET_NETWORK:
@@ -2541,32 +2328,33 @@
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
- if (lastForgetConfigurationAttempt != null) {
- sb.append(" ").append(lastForgetConfigurationAttempt.configKey());
- sb.append(" nid=").append(lastForgetConfigurationAttempt.networkId);
- if (lastForgetConfigurationAttempt.hiddenSSID) {
+ config = (WifiConfiguration) msg.obj;
+ if (config != null) {
+ sb.append(" ").append(config.configKey());
+ sb.append(" nid=").append(config.networkId);
+ if (config.hiddenSSID) {
sb.append(" hidden");
}
- if (lastForgetConfigurationAttempt.preSharedKey != null) {
+ if (config.preSharedKey != null) {
sb.append(" hasPSK");
}
- if (lastForgetConfigurationAttempt.ephemeral) {
+ if (config.ephemeral) {
sb.append(" ephemeral");
}
- if (lastForgetConfigurationAttempt.selfAdded) {
+ if (config.selfAdded) {
sb.append(" selfAdded");
}
- sb.append(" cuid=").append(lastForgetConfigurationAttempt.creatorUid);
- sb.append(" suid=").append(lastForgetConfigurationAttempt.lastUpdateUid);
+ sb.append(" cuid=").append(config.creatorUid);
+ sb.append(" suid=").append(config.lastUpdateUid);
WifiConfiguration.NetworkSelectionStatus netWorkSelectionStatus =
- lastForgetConfigurationAttempt.getNetworkSelectionStatus();
+ config.getNetworkSelectionStatus();
sb.append(" ajst=").append(
netWorkSelectionStatus.getNetworkStatusString());
}
break;
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
sb.append(" ");
- sb.append(Integer.toString(msg.arg1));
+ sb.append(" timedOut=" + Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
String bssid = (String) msg.obj;
@@ -2590,7 +2378,7 @@
sb.append(String.format(" bcn=%d", mRunningBeaconCount));
sb.append(String.format(" con=%d", mConnectionReqCount));
sb.append(String.format(" untrustedcn=%d", mUntrustedReqCount));
- key = mWifiConfigManager.getLastSelectedConfiguration();
+ key = mWifiConfigManager.getLastSelectedNetworkConfigKey();
if (key != null) {
sb.append(" last=").append(key);
}
@@ -2608,7 +2396,7 @@
if (config != null) {
sb.append(" ").append(config.configKey());
}
- key = mWifiConfigManager.getLastSelectedConfiguration();
+ key = mWifiConfigManager.getLastSelectedNetworkConfigKey();
if (key != null) {
sb.append(" last=").append(key);
}
@@ -2625,7 +2413,7 @@
if (mTargetRoamBSSID != null) {
sb.append(" Target=").append(mTargetRoamBSSID);
}
- sb.append(" roam=").append(Boolean.toString(mAutoRoaming));
+ sb.append(" roam=").append(Boolean.toString(mIsAutoRoaming));
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
if (msg.obj != null) {
@@ -2640,44 +2428,10 @@
sb.append(" freq=").append(mWifiInfo.getFrequency());
sb.append(" rssi=").append(mWifiInfo.getRssi());
}
- if (linkDebouncing) {
+ if (isLinkDebouncing()) {
sb.append(" debounce");
}
break;
- case WifiMonitor.SSID_TEMP_DISABLED:
- case WifiMonitor.SSID_REENABLED:
- sb.append(" nid=").append(msg.arg1);
- if (msg.obj != null) {
- sb.append(" ").append((String) msg.obj);
- }
- config = getCurrentWifiConfiguration();
- if (config != null) {
- WifiConfiguration.NetworkSelectionStatus netWorkSelectionStatus =
- config.getNetworkSelectionStatus();
- sb.append(" cur=").append(config.configKey());
- sb.append(" ajst=").append(netWorkSelectionStatus.getNetworkStatusString());
- if (config.selfAdded) {
- sb.append(" selfAdded");
- }
- if (config.status != 0) {
- sb.append(" st=").append(config.status);
- sb.append(" rs=").append(
- netWorkSelectionStatus.getNetworkDisableReasonString());
- }
- if (config.lastConnected != 0) {
- now = System.currentTimeMillis();
- sb.append(" lastconn=").append(now - config.lastConnected).append("(ms)");
- }
- if (mLastBssid != null) {
- sb.append(" lastbssid=").append(mLastBssid);
- }
- if (mWifiInfo.getFrequency() != -1) {
- sb.append(" freq=").append(mWifiInfo.getFrequency());
- sb.append(" rssi=").append(mWifiInfo.getRssi());
- sb.append(" bssid=").append(mWifiInfo.getBSSID());
- }
- }
- break;
case CMD_RSSI_POLL:
case CMD_UNWANTED_NETWORK:
case WifiManager.RSSI_PKTCNT_FETCH:
@@ -2703,22 +2457,17 @@
if (report != null) {
sb.append(" ").append(report);
}
- if (mWifiScoreReport != null) {
- sb.append(mWifiScoreReport.getReport());
- }
- if (mConnectedModeGScanOffloadStarted) {
- sb.append(" offload-started periodMilli " + mGScanPeriodMilli);
- } else {
- sb.append(" offload-stopped");
+ if (mWifiScoreReport.isLastReportValid()) {
+ sb.append(mWifiScoreReport.getLastReport());
}
break;
- case CMD_AUTO_CONNECT:
+ case CMD_START_CONNECT:
case WifiManager.CONNECT_NETWORK:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
- config = mWifiConfigManager.getWifiConfiguration(msg.arg1);
+ config = mWifiConfigManager.getConfiguredNetwork(msg.arg1);
if (config != null) {
sb.append(" ").append(config.configKey());
if (config.visibility != null) {
@@ -2728,7 +2477,7 @@
if (mTargetRoamBSSID != null) {
sb.append(" ").append(mTargetRoamBSSID);
}
- sb.append(" roam=").append(Boolean.toString(mAutoRoaming));
+ sb.append(" roam=").append(Boolean.toString(mIsAutoRoaming));
config = getCurrentWifiConfiguration();
if (config != null) {
sb.append(config.configKey());
@@ -2737,14 +2486,14 @@
}
}
break;
- case CMD_AUTO_ROAM:
+ case CMD_START_ROAM:
sb.append(" ");
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
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);
@@ -2758,7 +2507,7 @@
if (mTargetRoamBSSID != null) {
sb.append(" ").append(mTargetRoamBSSID);
}
- sb.append(" roam=").append(Boolean.toString(mAutoRoaming));
+ sb.append(" roam=").append(Boolean.toString(mIsAutoRoaming));
sb.append(" fail count=").append(Integer.toString(mRoamFailCount));
break;
case CMD_ADD_OR_UPDATE_NETWORK:
@@ -2792,11 +2541,11 @@
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
- key = mWifiConfigManager.getLastSelectedConfiguration();
+ key = mWifiConfigManager.getLastSelectedNetworkConfigKey();
if (key != null) {
sb.append(" last=").append(key);
}
- config = mWifiConfigManager.getWifiConfiguration(msg.arg1);
+ config = mWifiConfigManager.getConfiguredNetwork(msg.arg1);
if (config != null && (key == null || !config.configKey().equals(key))) {
sb.append(" target=").append(key);
}
@@ -2806,14 +2555,7 @@
sb.append(Integer.toString(msg.arg1));
sb.append(" ");
sb.append(Integer.toString(msg.arg2));
- sb.append(" num=").append(mWifiConfigManager.getConfiguredNetworksSize());
- break;
- case CMD_HAS_CARRIER_CONFIGURED_NETWORKS:
- sb.append(" ");
- sb.append(Integer.toString(msg.arg1));
- sb.append(" ");
- sb.append(Integer.toString(msg.arg2));
- sb.append(" hasCarrierNetworks=").append(mWifiConfigManager.hasCarrierNetworks());
+ sb.append(" num=").append(mWifiConfigManager.getConfiguredNetworks().size());
break;
case DhcpClient.CMD_PRE_DHCP_ACTION:
sb.append(" ");
@@ -2870,7 +2612,8 @@
sb.append(" failures: ");
sb.append(Integer.toString(count));
sb.append("/");
- sb.append(Integer.toString(mWifiConfigManager.getMaxDhcpRetries()));
+ sb.append(Integer.toString(mFacade.getIntegerSetting(
+ mContext, Settings.Global.WIFI_MAX_DHCP_RETRY_COUNT, 0)));
if (mWifiInfo.getBSSID() != null) {
sb.append(" ").append(mWifiInfo.getBSSID());
}
@@ -2911,6 +2654,13 @@
sb.append(Integer.toString(msg.arg2));
sb.append(" cur=").append(disconnectingWatchdogCount);
break;
+ case CMD_DISABLE_P2P_WATCHDOG_TIMER:
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg1));
+ sb.append(" ");
+ sb.append(Integer.toString(msg.arg2));
+ sb.append(" cur=").append(mDisableP2pWatchdogCount);
+ break;
case CMD_START_RSSI_MONITORING_OFFLOAD:
case CMD_STOP_RSSI_MONITORING_OFFLOAD:
case CMD_RSSI_THRESHOLD_BREACH:
@@ -2956,7 +2706,7 @@
private void handleScreenStateChanged(boolean screenOn) {
mScreenOn = screenOn;
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd(" handleScreenStateChanged Enter: screenOn=" + screenOn
+ " mUserWantsSuspendOpt=" + mUserWantsSuspendOpt
+ " state " + getCurrentState().getName()
@@ -2976,9 +2726,8 @@
sendMessage(CMD_SET_SUSPEND_OPT_ENABLED, 1, shouldReleaseWakeLock);
}
}
- mScreenBroadcastReceived.set(true);
- getWifiLinkLayerStats(false);
+ getWifiLinkLayerStats();
mOnTimeScreenStateChange = mOnTime;
lastScreenStateChangeTimeStamp = lastLinkLayerStatsUpdate;
@@ -2988,7 +2737,7 @@
mWifiConnectivityManager.handleScreenStateChanged(screenOn);
}
- if (DBG) log("handleScreenStateChanged Exit: " + screenOn);
+ if (mVerboseLoggingEnabled) log("handleScreenStateChanged Exit: " + screenOn);
}
private void checkAndSetConnectivityInstance() {
@@ -2997,28 +2746,8 @@
}
}
-
- /**
- * Set the frequency band from the system setting value, if any.
- */
- private void setFrequencyBand() {
- int band = WifiManager.WIFI_FREQUENCY_BAND_AUTO;
-
- if (mWifiNative.setBand(band)) {
- mFrequencyBand.set(band);
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.setUserPreferredBand(band);
- }
- if (DBG) {
- logd("done set frequency band " + band);
- }
- } else {
- loge("Failed to set frequency band " + band);
- }
- }
-
private void setSuspendOptimizationsNative(int reason, boolean enabled) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("setSuspendOptimizationsNative: " + reason + " " + enabled
+ " -want " + mUserWantsSuspendOpt.get()
+ " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName()
@@ -3032,7 +2761,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()
@@ -3048,13 +2777,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) {
@@ -3072,7 +2801,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);
@@ -3081,7 +2810,7 @@
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
- private void setWifiApState(int wifiApState, int reason) {
+ private void setWifiApState(int wifiApState, int reason, String ifaceName, int mode) {
final int previousWifiApState = mWifiApState.get();
try {
@@ -3097,7 +2826,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);
@@ -3108,6 +2837,12 @@
intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
}
+ if (ifaceName == null) {
+ loge("Updating wifiApState with a null iface name");
+ }
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, ifaceName);
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mode);
+
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
@@ -3122,7 +2857,7 @@
return;
}
- mWifiConfigManager.trimANQPCache(false);
+ // TODO(b/31065385): mWifiConfigManager.trimANQPCache(false);
boolean connected = mLastBssid != null;
long activeBssid = 0L;
@@ -3135,45 +2870,17 @@
}
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
- // Frame), these scanResult DTIM's are negative and ignored.
- // <TODO> Cache these per BSSID, since dtim can change vary
- NetworkDetail networkDetail = resultDetail.getNetworkDetail();
- if (networkDetail != null && networkDetail.getDtimInterval() > 0) {
- List<WifiConfiguration> associatedWifiConfigurations =
- mWifiConfigManager.getSavedNetworkFromScanDetail(resultDetail);
- if (associatedWifiConfigurations != null) {
- for (WifiConfiguration associatedConf : associatedWifiConfigurations) {
- if (associatedConf != null) {
- associatedConf.dtimInterval = networkDetail.getDtimInterval();
- }
- }
- }
- }
- }
- 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,
// just try to reconnect to the same SSID by triggering a roam
// The third parameter 1 means roam not from network selection but debouncing
- sendMessage(CMD_AUTO_ROAM, mLastNetworkId, 1, null);
+ sendMessage(CMD_START_ROAM, mLastNetworkId, 1, null);
}
}
@@ -3184,29 +2891,16 @@
Integer newRssi = null;
Integer newLinkSpeed = null;
Integer newFrequency = null;
-
- String signalPoll = mWifiNative.signalPoll();
-
- if (signalPoll != null) {
- String[] lines = signalPoll.split("\n");
- for (String line : lines) {
- String[] prop = line.split("=");
- if (prop.length < 2) continue;
- try {
- if (prop[0].equals("RSSI")) {
- newRssi = Integer.parseInt(prop[1]);
- } else if (prop[0].equals("LINKSPEED")) {
- newLinkSpeed = Integer.parseInt(prop[1]);
- } else if (prop[0].equals("FREQUENCY")) {
- newFrequency = Integer.parseInt(prop[1]);
- }
- } catch (NumberFormatException e) {
- //Ignore, defaults on rssi and linkspeed are assigned
- }
- }
+ WifiNative.SignalPollResult pollResult = mWifiNative.signalPoll();
+ if (pollResult == null) {
+ return;
}
- if (DBG) {
+ newRssi = pollResult.currentRssi;
+ newLinkSpeed = pollResult.txBitrate;
+ newFrequency = pollResult.associationFrequency;
+
+ if (mVerboseLoggingEnabled) {
logd("fetchRssiLinkSpeedAndFrequencyNative rssi=" + newRssi +
" linkspeed=" + newLinkSpeed + " freq=" + newFrequency);
}
@@ -3219,10 +2913,6 @@
if (newRssi > 0) newRssi -= 256;
mWifiInfo.setRssi(newRssi);
/*
- * Log the rssi poll value in metrics
- */
- mWifiMetrics.incrementRssiPollRssiCount(newRssi);
- /*
* Rather then sending the raw RSSI out every time it
* changes, we precalculate the signal level that would
* be displayed in the status bar, and only send the
@@ -3255,7 +2945,13 @@
}
mWifiInfo.setFrequency(newFrequency);
}
- mWifiConfigManager.updateConfiguration(mWifiInfo);
+ mWifiConfigManager.updateScanDetailCacheFromWifiInfo(mWifiInfo);
+ /*
+ * Increment various performance metrics
+ */
+ if (newRssi != null && newLinkSpeed != null && newFrequency != null) {
+ mWifiMetrics.handlePollResult(mWifiInfo);
+ }
}
// Polling has completed, hence we wont have a score anymore
@@ -3264,46 +2960,11 @@
mWifiInfo.txSuccessRate = 0;
mWifiInfo.txRetriesRate = 0;
mWifiInfo.rxSuccessRate = 0;
- mWifiScoreReport = null;
- }
-
- // Object holding most recent wifi score report and bad Linkspeed count
- WifiScoreReport mWifiScoreReport = null;
-
- public double getTxPacketRate() {
- return mWifiInfo.txSuccessRate;
- }
-
- public double getRxPacketRate() {
- return mWifiInfo.rxSuccessRate;
- }
-
- /**
- * Fetch TX packet counters on current connection
- */
- private void fetchPktcntNative(RssiPacketCountInfo info) {
- String pktcntPoll = mWifiNative.pktcntPoll();
-
- if (pktcntPoll != null) {
- String[] lines = pktcntPoll.split("\n");
- for (String line : lines) {
- String[] prop = line.split("=");
- if (prop.length < 2) continue;
- try {
- if (prop[0].equals("TXGOOD")) {
- info.txgood = Integer.parseInt(prop[1]);
- } else if (prop[0].equals("TXBAD")) {
- info.txbad = Integer.parseInt(prop[1]);
- }
- } catch (NumberFormatException e) {
- // Ignore
- }
- }
- }
+ mWifiScoreReport.reset();
}
private void updateLinkProperties(LinkProperties newLp) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("Link configuration changed for netId: " + mLastNetworkId
+ " old: " + mLinkProperties + " new: " + newLp);
}
@@ -3319,7 +2980,7 @@
sendLinkConfigurationChangedBroadcast();
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
StringBuilder sb = new StringBuilder();
sb.append("updateLinkProperties nid: " + mLastNetworkId);
sb.append(" state: " + getNetworkDetailedState());
@@ -3358,7 +3019,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());
}
@@ -3366,6 +3027,7 @@
/* The gateway's MAC address is known */
if ((address == null) && (timeout > 0)) {
boolean reachable = false;
+ TrafficStats.setThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
try {
reachable = gateway.isReachable(timeout);
} catch (Exception e) {
@@ -3373,10 +3035,11 @@
+ gateway.getHostAddress());
} finally {
+ TrafficStats.clearThreadStatsTag();
if (reachable == true) {
address = macAddressFromRoute(gateway.getHostAddress());
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("updateDefaultRouteMacAddress reachable (tried again) :"
+ gateway.getHostAddress() + " found " + address);
}
@@ -3384,7 +3047,7 @@
}
}
if (address != null) {
- mWifiConfigManager.setDefaultGwMacAddress(mLastNetworkId, address);
+ mWifiConfigManager.setNetworkDefaultGwMacAddress(mLastNetworkId, address);
}
}
}
@@ -3392,13 +3055,6 @@
return address;
}
- void sendScanResultsAvailableBroadcast(boolean scanSucceeded) {
- Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
private void sendRssiChangeBroadcast(final int newRssi) {
try {
mBatteryStats.noteWifiRssiChanged(newRssi);
@@ -3475,7 +3131,7 @@
private boolean setNetworkDetailedState(NetworkInfo.DetailedState state) {
boolean hidden = false;
- if (linkDebouncing || isRoaming()) {
+ if (isLinkDebouncing() || mIsAutoRoaming) {
// 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.
@@ -3490,7 +3146,7 @@
//
hidden = true;
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("setDetailed state, old ="
+ mNetworkInfo.getDetailedState() + " and new state=" + state
+ " hidden=" + hidden);
@@ -3499,7 +3155,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());
@@ -3537,46 +3193,40 @@
// 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
if (SupplicantState.isConnecting(state)) {
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ mWifiInfo.setNetworkId(lookupFrameworkNetworkId(stateChangeResult.networkId));
} else {
mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
}
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);
+ WifiConfiguration config = getCurrentWifiConfiguration();
+ if (config != null) {
+ // Set meteredHint to true if the access network type of the connecting/connected AP
+ // is a chargeable public network.
+ ScanDetailCache scanDetailCache = mWifiConfigManager.getScanDetailCacheForNetwork(
+ config.networkId);
+ if (scanDetailCache != null) {
+ ScanDetail scanDetail = scanDetailCache.getScanDetail(stateChangeResult.BSSID);
+ if (scanDetail != null) {
+ NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+ if (networkDetail != null
+ && networkDetail.getAnt() == NetworkDetail.Ant.ChargeablePublic) {
+ mWifiInfo.setMeteredHint(true);
+ }
}
}
- }
- mWifiInfo.setSSID(stateChangeResult.wifiSsid);
- mWifiInfo.setEphemeral(mWifiConfigManager.isEphemeral(mWifiInfo.getNetworkId()));
- if (!mWifiInfo.getMeteredHint()) { // don't override the value if already set.
- mWifiInfo.setMeteredHint(mWifiConfigManager.getMeteredHint(mWifiInfo.getNetworkId()));
+ mWifiInfo.setEphemeral(config.ephemeral);
+ if (!mWifiInfo.getMeteredHint()) { // don't override the value if already set.
+ mWifiInfo.setMeteredHint(config.meteredHint);
+ }
}
mSupplicantStateTracker.sendMessage(Message.obtain(message));
@@ -3589,31 +3239,32 @@
* 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();
- clearCurrentConfigBSSID("handleNetworkDisconnect");
+ clearTargetBssid("handleNetworkDisconnect");
stopIpManager();
/* Reset data structures */
- mWifiScoreReport = null;
+ mWifiScoreReport.reset();
mWifiInfo.reset();
- linkDebouncing = false;
+ mIsLinkDebouncing = false;
/* Reset roaming parameters */
- mAutoRoaming = false;
+ mIsAutoRoaming = false;
setNetworkDetailedState(DetailedState.DISCONNECTED);
if (mNetworkAgent != null) {
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent = null;
}
- mWifiConfigManager.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
/* Clear network properties */
clearLinkProperties();
@@ -3621,8 +3272,6 @@
/* Cend event to CM & network change broadcast */
sendNetworkStateChangeBroadcast(mLastBssid);
- /* Cancel auto roam requests */
- autoRoamSetBSSID(mLastNetworkId, "any");
mLastBssid = null;
registerDisconnected();
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
@@ -3633,7 +3282,10 @@
* or when the driver is hung. Ensure supplicant is stopped here.
*/
if (killSupplicant) {
- mWifiMonitor.killSupplicant(mP2pSupported);
+ mWifiMonitor.stopAllMonitoring();
+ if (!mWifiNative.disableSupplicant()) {
+ loge("Failed to disable supplicant after connection loss");
+ }
}
mWifiNative.closeSupplicantConnection();
sendSupplicantConnectionChangedBroadcast(false);
@@ -3660,7 +3312,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
@@ -3671,15 +3323,20 @@
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);
+ } else {
+ // If the p2p service is not running, we can proceed directly.
+ sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
+ }
}
void handlePostDhcpSetup() {
@@ -3687,32 +3344,64 @@
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);
+ }
+
+ private static final long DIAGS_CONNECT_TIMEOUT_MILLIS = 60 * 1000;
+ private long mDiagsConnectionStartMillis = -1;
+ /**
+ * Inform other components that a new connection attempt is starting.
+ */
+ private void reportConnectionAttemptStart(
+ WifiConfiguration config, String targetBSSID, int roamType) {
+ mWifiMetrics.startConnectionEvent(config, targetBSSID, roamType);
+ mDiagsConnectionStartMillis = mClock.getElapsedSinceBootMillis();
+ mWifiDiagnostics.reportConnectionEvent(
+ mDiagsConnectionStartMillis, WifiDiagnostics.CONNECTION_EVENT_STARTED);
+ // TODO(b/35329124): Remove CMD_DIAGS_CONNECT_TIMEOUT, once WifiStateMachine
+ // grows a proper CONNECTING state.
+ sendMessageDelayed(CMD_DIAGS_CONNECT_TIMEOUT,
+ mDiagsConnectionStartMillis, DIAGS_CONNECT_TIMEOUT_MILLIS);
}
/**
- * Inform other components (WifiMetrics, WifiLogger, etc.) that the current connection attempt
+ * Inform other components (WifiMetrics, WifiDiagnostics, etc.) that the current connection attempt
* has concluded.
*/
private void reportConnectionAttemptEnd(int level2FailureCode, int connectivityFailureCode) {
mWifiMetrics.endConnectionEvent(level2FailureCode, connectivityFailureCode);
switch (level2FailureCode) {
case WifiMetrics.ConnectionEvent.FAILURE_NONE:
+ // Ideally, we'd wait until IP reachability has been confirmed. this code falls
+ // short in two ways:
+ // - at the time of the CMD_IP_CONFIGURATION_SUCCESSFUL event, we don't know if we
+ // actually have ARP reachability. it might be better to wait until the wifi
+ // network has been validated by IpManager.
+ // - in the case of a roaming event (intra-SSID), we probably trigger when L2 is
+ // complete.
+ //
+ // TODO(b/34181219): Fix the above.
+ mWifiDiagnostics.reportConnectionEvent(
+ mDiagsConnectionStartMillis, WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
+ break;
case WifiMetrics.ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT:
- // WifiLogger doesn't care about success, or pre-empted connections.
+ case WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED:
+ // WifiDiagnostics doesn't care about pre-empted connections, or cases
+ // where we failed to initiate a connection attempt with supplicant.
break;
default:
- mWifiLogger.reportConnectionFailure();
+ mWifiDiagnostics.reportConnectionEvent(
+ mDiagsConnectionStartMillis, WifiDiagnostics.CONNECTION_EVENT_FAILED);
}
+ mDiagsConnectionStartMillis = -1;
}
private void handleIPv4Success(DhcpResults dhcpResults) {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("handleIPv4Success <" + dhcpResults.toString() + ">");
logd("link address " + dhcpResults.ipAddress);
}
@@ -3723,7 +3412,7 @@
addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
}
- if (isRoaming()) {
+ if (mIsAutoRoaming) {
int previousAddress = mWifiInfo.getIpAddress();
int newAddress = NetworkUtils.inetAddressToInt(addr);
if (previousAddress != newAddress) {
@@ -3757,12 +3446,6 @@
} else {
// Clear the per BSSID failure count
result.numIpConfigFailures = 0;
- // Clear the WHOLE BSSID blacklist, which means supplicant is free to retry
- // any BSSID, even though it may already have a non zero ip failure count,
- // this will typically happen if the user walks away and come back to his arrea
- // TODO: implement blacklisting based on a timer, i.e. keep BSSID blacklisted
- // in supplicant for a couple of hours or a day
- mWifiConfigManager.clearBssidBlacklist();
}
}
}
@@ -3770,8 +3453,8 @@
private void handleIPv4Failure() {
// 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) {
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_DHCP_FAILURE);
+ if (mVerboseLoggingEnabled) {
int count = -1;
WifiConfiguration config = getCurrentWifiConfiguration();
if (config != null) {
@@ -3788,7 +3471,7 @@
mDhcpResults.clear();
}
}
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd("handleIPv4Failure");
}
}
@@ -3817,97 +3500,6 @@
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;
- }
-
- 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");
- }
-
- try {
- mNwService.wifiFirmwareReload(mInterfaceName, "AP");
- if (DBG) Log.d(TAG, "Firmware reloaded in AP mode");
- } catch (Exception e) {
- Log.e(TAG, "Failed to reload AP firmware " + e);
- }
-
- if (!mWifiNative.startHal()) {
- /* starting HAL is optional */
- Log.e(TAG, "Failed to start HAL");
- }
- return true;
- }
-
- private byte[] macAddressFromString(String macString) {
- String[] macBytes = macString.split(":");
- if (macBytes.length != 6) {
- throw new IllegalArgumentException("MAC address should be 6 bytes long!");
- }
- byte[] mac = new byte[6];
- for (int i = 0; i < macBytes.length; i++) {
- Integer hexVal = Integer.parseInt(macBytes[i], 16);
- mac[i] = hexVal.byteValue();
- }
- return mac;
- }
-
/*
* Read a MAC address in /proc/arp/table, used by WifistateMachine
* so as to record MAC address of default gateway.
@@ -3987,6 +3579,7 @@
}
}
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("mConnectionReqCount " + mConnectionReqCount);
}
@@ -4032,6 +3625,7 @@
}
}
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("mUntrustedReqCount " + mUntrustedReqCount);
}
@@ -4056,11 +3650,33 @@
}
}
+ /**
+ * WifiStateMachine needs to enable/disable other services when wifi is in client mode. This
+ * method allows WifiStateMachine to get these additional system services.
+ *
+ * At this time, this method is used to setup variables for P2P service and Wifi Aware.
+ */
+ private void getAdditionalWifiServiceInterfaces() {
+ // First set up Wifi Direct
+ if (mP2pSupported) {
+ IBinder s1 = mFacade.getService(Context.WIFI_P2P_SERVICE);
+ WifiP2pServiceImpl wifiP2pServiceImpl =
+ (WifiP2pServiceImpl) IWifiP2pManager.Stub.asInterface(s1);
+
+ if (wifiP2pServiceImpl != null) {
+ mWifiP2pChannel = new AsyncChannel();
+ mWifiP2pChannel.connect(mContext, getHandler(),
+ wifiP2pServiceImpl.getP2pStateMachineMessenger());
+ }
+ }
+ }
+
/********************************************************
* HSM states
*******************************************************/
class DefaultState extends State {
+
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
@@ -4070,8 +3686,19 @@
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);
+ // since the p2p channel is connected, we should enable p2p if we are in
+ // connect mode. We may not be in connect mode yet, we may have just
+ // set the operational mode and started to set up for connect mode.
+ if (mOperationalMode == CONNECT_MODE) {
+ // This message will only be handled if we are in Connect mode.
+ // If we are not in connect mode yet, this will be dropped and the
+ // ConnectMode.enter method will call to enable p2p.
+ sendMessage(CMD_ENABLE_P2P);
+ }
} else {
+ // TODO: We should probably do some cleanup or attempt a retry
+ // b/34283611
loge("WifiP2pService connection failure, error=" + message.arg1);
}
} else {
@@ -4083,7 +3710,7 @@
AsyncChannel ac = (AsyncChannel) message.obj;
if (ac == mWifiP2pChannel) {
loge("WifiP2pService channel lost, message.arg1 =" + message.arg1);
- //TODO: Re-establish connection to state machine after a delay
+ //TODO: Re-establish connection to state machine after a delay (b/34283611)
// mWifiP2pChannel.connect(mContext, getHandler(),
// mWifiP2pManager.getMessenger());
}
@@ -4093,25 +3720,37 @@
mBluetoothConnectionActive = (message.arg1 !=
BluetoothAdapter.STATE_DISCONNECTED);
break;
- /* Synchronous call returns */
- case CMD_PING_SUPPLICANT:
case CMD_ENABLE_NETWORK:
+ boolean disableOthers = message.arg2 == 1;
+ int netId = message.arg1;
+ boolean ok = mWifiConfigManager.enableNetwork(
+ netId, disableOthers, message.sendingUid);
+ if (!ok) {
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+ }
+ replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ break;
case CMD_ADD_OR_UPDATE_NETWORK:
- case CMD_REMOVE_NETWORK:
+ WifiConfiguration config = (WifiConfiguration) message.obj;
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
+ if (!result.isSuccess()) {
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+ }
+ replyToMessage(message, message.what, result.getNetworkId());
+ break;
case CMD_SAVE_CONFIG:
replyToMessage(message, message.what, FAILURE);
break;
- case CMD_GET_CAPABILITY_FREQ:
- replyToMessage(message, message.what, null);
+ case CMD_REMOVE_NETWORK:
+ deleteNetworkConfigAndSendReply(message, false);
break;
case CMD_GET_CONFIGURED_NETWORKS:
- replyToMessage(message, message.what, (List<WifiConfiguration>) null);
- break;
- case CMD_HAS_CARRIER_CONFIGURED_NETWORKS:
- replyToMessage(message, message.what, null);
+ replyToMessage(message, message.what, mWifiConfigManager.getSavedNetworks());
break;
case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
- replyToMessage(message, message.what, (List<WifiConfiguration>) null);
+ replyToMessage(message, message.what,
+ mWifiConfigManager.getConfiguredNetworksWithPasswords());
break;
case CMD_ENABLE_RSSI_POLL:
mEnableRssiPolling = (message.arg1 == 1);
@@ -4123,7 +3762,16 @@
setSuspendOptimizations(SUSPEND_DUE_TO_HIGH_PERF, true);
}
break;
+ case CMD_INITIALIZE:
+ ok = mWifiNative.initializeVendorHal(mVendorHalDeathRecipient);
+ replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ break;
case CMD_BOOT_COMPLETED:
+ // get other services that we need to manage
+ getAdditionalWifiServiceInterfaces();
+ if (!mWifiConfigManager.loadFromStore()) {
+ Log.e(TAG, "Failed to load from config store");
+ }
maybeRegisterNetworkFactory();
break;
case CMD_SCREEN_STATE_CHANGED:
@@ -4135,9 +3783,6 @@
break;
case CMD_START_SUPPLICANT:
case CMD_STOP_SUPPLICANT:
- case CMD_STOP_SUPPLICANT_FAILED:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
case CMD_DRIVER_START_TIMED_OUT:
case CMD_START_AP:
case CMD_START_AP_FAILURE:
@@ -4157,31 +3802,26 @@
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
case WifiMonitor.WPS_OVERLAP_EVENT:
- case CMD_BLACKLIST_NETWORK:
- case CMD_CLEAR_BLACKLIST:
case CMD_SET_OPERATIONAL_MODE:
- case CMD_SET_FREQUENCY_BAND:
case CMD_RSSI_POLL:
- case CMD_ENABLE_ALL_NETWORKS:
case DhcpClient.CMD_PRE_DHCP_ACTION:
case DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE:
case DhcpClient.CMD_POST_DHCP_ACTION:
case CMD_NO_NETWORKS_PERIODIC_SCAN:
+ case CMD_ENABLE_P2P:
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:
- case CMD_AUTO_ROAM:
- case CMD_AUTO_SAVE_NETWORK:
+ case CMD_START_CONNECT:
+ case CMD_START_ROAM:
case CMD_ASSOCIATED_BSSID:
case CMD_UNWANTED_NETWORK:
case CMD_DISCONNECTING_WATCHDOG_TIMER:
case CMD_ROAM_WATCHDOG_TIMER:
+ case CMD_DISABLE_P2P_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:
@@ -4194,17 +3834,12 @@
setSuspendOptimizations(SUSPEND_DUE_TO_SCREEN, false);
}
break;
- case WifiMonitor.DRIVER_HUNG_EVENT:
- setSupplicantRunning(false);
- setSupplicantRunning(true);
- break;
case WifiManager.CONNECT_NETWORK:
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.BUSY);
break;
case WifiManager.FORGET_NETWORK:
- replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
- WifiManager.BUSY);
+ deleteNetworkConfigAndSendReply(message, true);
break;
case WifiManager.SAVE_NETWORK:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
@@ -4232,10 +3867,10 @@
replyToMessage(message, message.what, featureSet);
break;
case CMD_FIRMWARE_ALERT:
- if (mWifiLogger != null) {
+ if (mWifiDiagnostics != null) {
byte[] buffer = (byte[])message.obj;
int alertReason = message.arg1;
- mWifiLogger.captureAlertData(alertReason, buffer);
+ mWifiDiagnostics.captureAlertData(alertReason, buffer);
mWifiMetrics.incrementAlertReasonCount(alertReason);
}
break;
@@ -4289,10 +3924,20 @@
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case CMD_USER_SWITCH:
- mWifiConfigManager.handleUserSwitch(message.arg1);
+ Set<Integer> removedNetworkIds =
+ mWifiConfigManager.handleUserSwitch(message.arg1);
+ if (removedNetworkIds.contains(mTargetNetworkId) ||
+ removedNetworkIds.contains(mLastNetworkId)) {
+ // Disconnect and let autojoin reselect a new network
+ sendMessage(CMD_DISCONNECT);
+ }
break;
- case CMD_ADD_PASSPOINT_MO:
- case CMD_MODIFY_PASSPOINT_MO:
+ case CMD_USER_UNLOCK:
+ mWifiConfigManager.handleUserUnlock(message.arg1);
+ break;
+ case CMD_USER_STOP:
+ mWifiConfigManager.handleUserStop(message.arg1);
+ break;
case CMD_QUERY_OSU_ICON:
case CMD_MATCH_PROVIDER_NETWORK:
/* reply with arg1 = 0 - it returns API failure to the calling app
@@ -4300,6 +3945,20 @@
*/
replyToMessage(message, message.what);
break;
+ case CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG:
+ int addResult = mPasspointManager.addOrUpdateProvider(
+ (PasspointConfiguration) message.obj, message.arg1)
+ ? SUCCESS : FAILURE;
+ replyToMessage(message, message.what, addResult);
+ break;
+ case CMD_REMOVE_PASSPOINT_CONFIG:
+ int removeResult = mPasspointManager.removeProvider(
+ (String) message.obj) ? SUCCESS : FAILURE;
+ replyToMessage(message, message.what, removeResult);
+ break;
+ case CMD_GET_PASSPOINT_CONFIGS:
+ replyToMessage(message, message.what, mPasspointManager.getProviderConfigs());
+ break;
case CMD_RESET_SIM_NETWORKS:
/* Defer this message until supplicant is started. */
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
@@ -4315,6 +3974,20 @@
mWifiNative.stopFilteringMulticastV4Packets();
}
break;
+ case CMD_CLIENT_INTERFACE_BINDER_DEATH:
+ Log.e(TAG, "wificond died unexpectedly. Triggering recovery");
+ mWifiMetrics.incrementNumWificondCrashes();
+ mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_WIFICOND_CRASH);
+ break;
+ case CMD_VENDOR_HAL_HWBINDER_DEATH:
+ Log.e(TAG, "Vendor HAL died unexpectedly. Triggering recovery");
+ mWifiMetrics.incrementNumHalCrashes();
+ mWifiInjector.getSelfRecovery().trigger(SelfRecovery.REASON_HAL_CRASH);
+ break;
+ case CMD_DIAGS_CONNECT_TIMEOUT:
+ mWifiDiagnostics.reportConnectionEvent(
+ (Long) message.obj, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ break;
default:
loge("Error! unhandled message" + message);
break;
@@ -4324,99 +3997,74 @@
}
class InitialState extends State {
+
+ private void cleanup() {
+ // Tearing down the client interfaces below is going to stop our supplicant.
+ mWifiMonitor.stopAllMonitoring();
+
+ mDeathRecipient.unlinkToDeath();
+ mWifiNative.tearDown();
+ }
+
@Override
public void enter() {
- mWifiNative.stopHal();
- mWifiNative.unloadDriver();
- if (mWifiP2pChannel == null) {
- mWifiP2pChannel = new AsyncChannel();
- mWifiP2pChannel.connect(mContext, getHandler(),
- mWifiP2pServiceImpl.getP2pStateMachineMessenger());
- }
-
- if (mWifiApConfigStore == null) {
- mWifiApConfigStore =
- mFacade.makeApConfigStore(mContext, mBackupManagerProxy);
- }
+ mWifiStateTracker.updateState(WifiStateTracker.INVALID);
+ cleanup();
}
+
@Override
public boolean processMessage(Message message) {
logStateAndMessage(message, this);
switch (message.what) {
case CMD_START_SUPPLICANT:
- if (mWifiNative.loadDriver()) {
- try {
- mNwService.wifiFirmwareReload(mInterfaceName, "STA");
- } catch (Exception e) {
- loge("Failed to reload STA firmware " + e);
- setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
- return HANDLED;
- }
-
- try {
- // A runtime crash can leave the interface up and
- // IP addresses configured, and this affects
- // connectivity when supplicant starts up.
- // Ensure interface is down and we have no IP
- // addresses before a supplicant start.
- mNwService.setInterfaceDown(mInterfaceName);
- mNwService.clearInterfaceAddresses(mInterfaceName);
-
- // Set privacy extensions
- mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
-
- // IPv6 is enabled only as long as access point is connected since:
- // - IPv6 addresses and routes stick around after disconnection
- // - kernel is unaware when connected and fails to start IPv6 negotiation
- // - kernel can start autoconfiguration when 802.1x is not complete
- mNwService.disableIpv6(mInterfaceName);
- } catch (RemoteException re) {
- loge("Unable to change interface settings: " + re);
- } catch (IllegalStateException ie) {
- loge("Unable to change interface settings: " + ie);
- }
-
- /* Stop a running supplicant after a runtime restart
- * Avoids issues with drivers that do not handle interface down
- * on a running supplicant properly.
- */
- mWifiMonitor.killSupplicant(mP2pSupported);
-
- if (mWifiNative.startHal() == false) {
- /* starting HAL is optional */
- loge("Failed to start HAL");
- }
-
- if (mWifiNative.startSupplicant(mP2pSupported)) {
- setSupplicantLogLevel();
- setWifiState(WIFI_STATE_ENABLING);
- if (DBG) log("Supplicant start successful");
- mWifiMonitor.startMonitoring(mInterfaceName);
- transitionTo(mSupplicantStartingState);
- } else {
- loge("Failed to start supplicant!");
- setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
- }
- } else {
- loge("Failed to load driver");
+ mClientInterface = mWifiNative.setupForClientMode();
+ if (mClientInterface == null
+ || !mDeathRecipient.linkToDeath(mClientInterface.asBinder())) {
setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
+ cleanup();
+ break;
}
+
+ try {
+ // A runtime crash or shutting down AP mode can leave
+ // IP addresses configured, and this affects
+ // connectivity when supplicant starts up.
+ // Ensure we have no IP addresses before a supplicant start.
+ mNwService.clearInterfaceAddresses(mInterfaceName);
+
+ // Set privacy extensions
+ mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
+
+ // IPv6 is enabled only as long as access point is connected since:
+ // - IPv6 addresses and routes stick around after disconnection
+ // - kernel is unaware when connected and fails to start IPv6 negotiation
+ // - kernel can start autoconfiguration when 802.1x is not complete
+ mNwService.disableIpv6(mInterfaceName);
+ } catch (RemoteException re) {
+ loge("Unable to change interface settings: " + re);
+ } catch (IllegalStateException ie) {
+ loge("Unable to change interface settings: " + ie);
+ }
+
+ if (!mWifiNative.enableSupplicant()) {
+ loge("Failed to start supplicant!");
+ setWifiState(WifiManager.WIFI_STATE_UNKNOWN);
+ cleanup();
+ break;
+ }
+ if (mVerboseLoggingEnabled) log("Supplicant start successful");
+ mWifiMonitor.startMonitoring(mInterfaceName, true);
+ setSupplicantLogLevel();
+ transitionTo(mSupplicantStartingState);
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);
- }
+ transitionTo(mSoftApState);
break;
case CMD_SET_OPERATIONAL_MODE:
mOperationalMode = message.arg1;
+ if (mOperationalMode != DISABLED_MODE) {
+ sendMessage(CMD_START_SUPPLICANT);
+ }
break;
default:
return NOT_HANDLED;
@@ -4462,7 +4110,7 @@
switch(message.what) {
case WifiMonitor.SUP_CONNECTION_EVENT:
- if (DBG) log("Supplicant connection established");
+ if (mVerboseLoggingEnabled) log("Supplicant connection established");
mSupplicantRestartCount = 0;
/* Reset the supplicant state to indicate the supplicant
@@ -4474,22 +4122,19 @@
mLastSignalLevel = -1;
mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
- /* set frequency band of operation */
- setFrequencyBand();
- mWifiNative.enableSaveConfig();
- mWifiConfigManager.loadAndEnableAllNetworks();
- if (mWifiConfigManager.mEnableVerboseLogging.get() > 0) {
- enableVerboseLogging(mWifiConfigManager.mEnableVerboseLogging.get());
+ // Attempt to migrate data out of legacy store.
+ if (!mWifiConfigManager.migrateFromLegacyStore()) {
+ Log.e(TAG, "Failed to migrate from legacy config store");
}
initializeWpsDetails();
-
sendSupplicantConnectionChangedBroadcast(true);
- transitionTo(mDriverStartedState);
+ transitionTo(mSupplicantStartedState);
break;
case WifiMonitor.SUP_DISCONNECTION_EVENT:
if (++mSupplicantRestartCount <= SUPPLICANT_RESTART_TRIES) {
loge("Failed to setup control channel, restart supplicant");
- mWifiMonitor.killSupplicant(mP2pSupported);
+ mWifiMonitor.stopAllMonitoring();
+ mWifiNative.disableSupplicant();
transitionTo(mInitialState);
sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
} else {
@@ -4504,10 +4149,7 @@
case CMD_STOP_SUPPLICANT:
case CMD_START_AP:
case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
case CMD_SET_OPERATIONAL_MODE:
- case CMD_SET_FREQUENCY_BAND:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
deferMessage(message);
break;
@@ -4521,22 +4163,86 @@
class SupplicantStartedState extends State {
@Override
public void enter() {
- int defaultInterval = mContext.getResources().getInteger(
- R.integer.config_wifi_supplicant_scan_interval);
+ if (mVerboseLoggingEnabled) {
+ logd("SupplicantStartedState enter");
+ }
- mSupplicantScanIntervalMs = mFacade.getLongSetting(mContext,
- Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,
- defaultInterval);
-
- mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);
mWifiNative.setExternalSim(true);
- /* turn on use of DFS channels */
- mWifiNative.setDfsFlag(true);
-
setRandomMacOui();
- mWifiNative.enableAutoConnect(false);
mCountryCode.setReadyForChange(true);
+
+ // We can't do this in the constructor because WifiStateMachine is created before the
+ // wifi scanning service is initialized
+ if (mWifiScanner == null) {
+ mWifiScanner = mWifiInjector.getWifiScanner();
+
+ synchronized (mWifiReqCountLock) {
+ mWifiConnectivityManager =
+ mWifiInjector.makeWifiConnectivityManager(mWifiInfo,
+ hasConnectionRequests());
+ mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0);
+ mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);
+ }
+ }
+
+ mWifiDiagnostics.startLogging(mVerboseLoggingEnabled);
+ mIsRunning = true;
+ updateBatteryWorkSource(null);
+ /**
+ * Enable bluetooth coexistence scan mode when bluetooth connection is active.
+ * When this mode is on, some of the low-level scan parameters used by the
+ * driver are changed to reduce interference with bluetooth
+ */
+ mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
+ // initialize network state
+ setNetworkDetailedState(DetailedState.DISCONNECTED);
+
+ // Disable legacy multicast filtering, which on some chipsets defaults to enabled.
+ // Legacy IPv6 multicast filtering blocks ICMPv6 router advertisements which breaks IPv6
+ // provisioning. Legacy IPv4 multicast filtering may be re-enabled later via
+ // IpManager.Callback.setFallbackMulticastFilter()
+ mWifiNative.stopFilteringMulticastV4Packets();
+ mWifiNative.stopFilteringMulticastV6Packets();
+
+ if (mOperationalMode == SCAN_ONLY_MODE ||
+ mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
+ mWifiNative.disconnect();
+ setWifiState(WIFI_STATE_DISABLED);
+ transitionTo(mScanModeState);
+ } else if (mOperationalMode == CONNECT_MODE) {
+ setWifiState(WIFI_STATE_ENABLING);
+ // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin
+ transitionTo(mDisconnectedState);
+ } else if (mOperationalMode == DISABLED_MODE) {
+ transitionTo(mSupplicantStoppingState);
+ }
+
+ // Set the right suspend mode settings
+ mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
+ && mUserWantsSuspendOpt.get());
+
+ mWifiNative.setPowerSave(true);
+
+ if (mP2pSupported) {
+ if (mOperationalMode == CONNECT_MODE) {
+ p2pSendMessage(WifiStateMachine.CMD_ENABLE_P2P);
+ } else {
+ // 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.
+ }
+ }
+
+ 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);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+
+ // Disable wpa_supplicant from auto reconnecting.
+ mWifiNative.enableStaAutoReconnect(false);
+ // STA has higher priority over P2P
+ mWifiNative.setConcurrencyPriority(true);
}
@Override
@@ -4563,39 +4269,32 @@
}
sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS);
break;
+ case CMD_START_SCAN:
+ // TODO: remove scan request path (b/31445200)
+ handleScanRequest(message);
+ break;
case WifiMonitor.SCAN_RESULTS_EVENT:
case WifiMonitor.SCAN_FAILED_EVENT:
+ // TODO: remove handing of SCAN_RESULTS_EVENT and SCAN_FAILED_EVENT when scan
+ // results are retrieved from WifiScanner (b/31444878)
maybeRegisterNetworkFactory(); // Make sure our NetworkFactory is registered
setScanResults();
- if (mIsFullScanOngoing || mSendScanResultsBroadcast) {
- /* Just updated results from full scan, let apps know about this */
- boolean scanSucceeded = message.what == WifiMonitor.SCAN_RESULTS_EVENT;
- sendScanResultsAvailableBroadcast(scanSucceeded);
- }
- mSendScanResultsBroadcast = false;
mIsScanOngoing = false;
mIsFullScanOngoing = false;
if (mBufferedScanMsg.size() > 0)
sendMessage(mBufferedScanMsg.remove());
break;
- case CMD_PING_SUPPLICANT:
- boolean ok = mWifiNative.ping();
- replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
- break;
- case CMD_GET_CAPABILITY_FREQ:
- String freqs = mWifiNative.getFreqCapability();
- replyToMessage(message, message.what, freqs);
- break;
case CMD_START_AP:
/* Cannot start soft AP while in client mode */
loge("Failed to start soft AP with a running supplicant");
- setWifiApState(WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL);
+ setWifiApState(WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL,
+ null, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
break;
case CMD_SET_OPERATIONAL_MODE:
mOperationalMode = message.arg1;
- mWifiConfigManager.
- setAndEnableLastSelectedConfiguration(
- WifiConfiguration.INVALID_NETWORK_ID);
+ if (mOperationalMode == DISABLED_MODE) {
+ transitionTo(mSupplicantStoppingState);
+ }
break;
case CMD_TARGET_BSSID:
// Trying to associate to this BSSID
@@ -4604,307 +4303,18 @@
}
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:
log("resetting EAP-SIM/AKA/AKA' networks since SIM was changed");
mWifiConfigManager.resetSimNetworks();
break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
-
- @Override
- public void exit() {
- mNetworkInfo.setIsAvailable(false);
- if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
- mCountryCode.setReadyForChange(false);
- }
- }
-
- class SupplicantStoppingState extends State {
- @Override
- public void enter() {
- /* Send any reset commands to supplicant before shutting it down */
- handleNetworkDisconnect();
-
- String suppState = System.getProperty("init.svc.wpa_supplicant");
- if (suppState == null) suppState = "unknown";
- String p2pSuppState = System.getProperty("init.svc.p2p_supplicant");
- if (p2pSuppState == null) p2pSuppState = "unknown";
-
- logd("SupplicantStoppingState: stopSupplicant "
- + " init.svc.wpa_supplicant=" + suppState
- + " init.svc.p2p_supplicant=" + p2pSuppState);
- mWifiMonitor.stopSupplicant();
-
- /* Send ourselves a delayed message to indicate failure after a wait time */
- sendMessageDelayed(obtainMessage(CMD_STOP_SUPPLICANT_FAILED,
- ++mSupplicantStopFailureToken, 0), SUPPLICANT_RESTART_INTERVAL_MSECS);
- setWifiState(WIFI_STATE_DISABLING);
- mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
- }
- @Override
- public boolean processMessage(Message message) {
- logStateAndMessage(message, this);
-
- switch(message.what) {
- case WifiMonitor.SUP_CONNECTION_EVENT:
- loge("Supplicant connection received while stopping");
- break;
- case WifiMonitor.SUP_DISCONNECTION_EVENT:
- if (DBG) log("Supplicant connection lost");
- handleSupplicantConnectionLoss(false);
- transitionTo(mInitialState);
- break;
- case CMD_STOP_SUPPLICANT_FAILED:
- if (message.arg1 == mSupplicantStopFailureToken) {
- loge("Timed out on a supplicant stop, kill and proceed");
- handleSupplicantConnectionLoss(true);
- transitionTo(mInitialState);
- }
- break;
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_OPERATIONAL_MODE:
- case CMD_SET_FREQUENCY_BAND:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class DriverStartingState extends State {
- private int mTries;
- @Override
- public void enter() {
- mTries = 1;
- /* Send ourselves a delayed message to start driver a second time */
- sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
- ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
- }
- @Override
- public boolean processMessage(Message message) {
- logStateAndMessage(message, this);
-
- switch(message.what) {
- case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
- SupplicantState state = handleSupplicantStateChange(message);
- /* If suplicant is exiting out of INTERFACE_DISABLED state into
- * a state that indicates driver has started, it is ready to
- * receive driver commands
- */
- if (SupplicantState.isDriverActive(state)) {
- transitionTo(mDriverStartedState);
- }
- break;
- case CMD_DRIVER_START_TIMED_OUT:
- if (message.arg1 == mDriverStartToken) {
- if (mTries >= 2) {
- loge("Failed to start driver after " + mTries);
- setSupplicantRunning(false);
- setSupplicantRunning(true);
- } else {
- loge("Driver start failed, retrying");
- mWakeLock.acquire();
- mWifiNative.startDriver();
- mWakeLock.release();
-
- ++mTries;
- /* Send ourselves a delayed message to start driver again */
- sendMessageDelayed(obtainMessage(CMD_DRIVER_START_TIMED_OUT,
- ++mDriverStartToken, 0), DRIVER_START_TIME_OUT_MSECS);
- }
- }
- break;
- /* Queue driver commands & connection events */
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case WifiMonitor.NETWORK_CONNECTION_EVENT:
- case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
- case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
- case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
- case WifiMonitor.WPS_OVERLAP_EVENT:
- case CMD_SET_FREQUENCY_BAND:
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_REASSOCIATE:
- case CMD_RECONNECT:
- messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
- deferMessage(message);
- break;
- case WifiMonitor.SCAN_RESULTS_EVENT:
- case WifiMonitor.SCAN_FAILED_EVENT:
- // Loose scan results obtained in Driver Starting state, they can only confuse
- // the state machine
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class DriverStartedState extends State {
- @Override
- public void enter() {
- if (DBG) {
- logd("DriverStartedState enter");
- }
-
- // We can't do this in the constructor because WifiStateMachine is created before the
- // wifi scanning service is initialized
- if (mWifiScanner == null) {
- mWifiScanner = mFacade.makeWifiScanner(mContext, getHandler().getLooper());
-
- synchronized (mWifiReqCountLock) {
- mWifiConnectivityManager = new WifiConnectivityManager(mContext,
- WifiStateMachine.this, mWifiScanner, mWifiConfigManager, mWifiInfo,
- mWifiQualifiedNetworkSelector, mWifiInjector,
- getHandler().getLooper(), hasConnectionRequests());
- mWifiConnectivityManager.setUntrustedConnectionAllowed(mUntrustedReqCount > 0);
- }
- }
-
- mWifiLogger.startLogging(DBG);
- mIsRunning = true;
- updateBatteryWorkSource(null);
- /**
- * Enable bluetooth coexistence scan mode when bluetooth connection is active.
- * When this mode is on, some of the low-level scan parameters used by the
- * driver are changed to reduce interference with bluetooth
- */
- mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
- /* initialize network state */
- setNetworkDetailedState(DetailedState.DISCONNECTED);
-
- // Disable legacy multicast filtering, which on some chipsets defaults to enabled.
- // Legacy IPv6 multicast filtering blocks ICMPv6 router advertisements which breaks IPv6
- // provisioning. Legacy IPv4 multicast filtering may be re-enabled later via
- // IpManager.Callback.setFallbackMulticastFilter()
- mWifiNative.stopFilteringMulticastV4Packets();
- mWifiNative.stopFilteringMulticastV6Packets();
-
- if (mOperationalMode != CONNECT_MODE) {
- mWifiNative.disconnect();
- mWifiConfigManager.disableAllNetworksNative();
- if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
- setWifiState(WIFI_STATE_DISABLED);
- }
- transitionTo(mScanModeState);
- } else {
-
- // 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
- // 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());
- } else {
- // Set the right suspend mode settings
- mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0
- && mUserWantsSuspendOpt.get());
-
- // Inform WifiConnectivtyManager the screen state in case
- // WifiConnectivityManager missed the last screen update because
- // it was not started yet.
- mWifiConnectivityManager.handleScreenStateChanged(mScreenOn);
- }
- mWifiNative.setPowerSave(true);
-
- if (mP2pSupported) {
- if (mOperationalMode == CONNECT_MODE) {
- mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
- } else {
- // P2P statemachine 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.
- }
- }
-
- 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);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-
- // Enable link layer stats gathering
- mWifiNative.setWifiLinkLayerStats("wlan0", 1);
- }
-
- @Override
- public boolean processMessage(Message message) {
- logStateAndMessage(message, this);
-
- switch(message.what) {
- case CMD_START_SCAN:
- handleScanRequest(message);
- break;
- case CMD_SET_FREQUENCY_BAND:
- int band = message.arg1;
- if (DBG) log("set frequency band " + band);
- if (mWifiNative.setBand(band)) {
-
- if (DBG) 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);
-
- } else {
- loge("Failed to set frequency band " + band);
- }
- break;
case CMD_BLUETOOTH_ADAPTER_STATE_CHANGE:
mBluetoothConnectionActive = (message.arg1 !=
BluetoothAdapter.STATE_DISCONNECTED);
mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);
break;
- case CMD_STOP_DRIVER:
- int mode = message.arg1;
-
- log("stop driver");
- mWifiConfigManager.disableAllNetworksNative();
-
- if (getCurrentState() != mDisconnectedState) {
- mWifiNative.disconnect();
- handleNetworkDisconnect();
- }
- mWakeLock.acquire();
- mWifiNative.stopDriver();
- mWakeLock.release();
- if (mP2pSupported) {
- transitionTo(mWaitForP2pDisableState);
- } else {
- transitionTo(mDriverStoppingState);
- }
- break;
- case CMD_START_DRIVER:
- if (mOperationalMode == CONNECT_MODE) {
- mWifiConfigManager.enableAllNetworks();
- }
- break;
case CMD_SET_SUSPEND_OPT_ENABLED:
if (message.arg1 == 1) {
setSuspendOptimizationsNative(SUSPEND_DUE_TO_SCREEN, true);
@@ -4930,7 +4340,8 @@
}
break;
case WifiMonitor.ANQP_DONE_EVENT:
- mWifiConfigManager.notifyANQPDone((Long) message.obj, message.arg1 != 0);
+ // TODO(zqiu): remove this when switch over to wificond for ANQP requests.
+ mPasspointManager.notifyANQPDone((AnqpEvent) message.obj);
break;
case CMD_STOP_IP_PACKET_OFFLOAD: {
int slot = message.arg1;
@@ -4941,29 +4352,28 @@
break;
}
case WifiMonitor.RX_HS20_ANQP_ICON_EVENT:
- mWifiConfigManager.notifyIconReceived((IconEvent) message.obj);
+ // TODO(zqiu): remove this when switch over to wificond for icon requests.
+ mPasspointManager.notifyIconDone((IconEvent) message.obj);
break;
case WifiMonitor.HS20_REMEDIATION_EVENT:
- wnmFrameReceived((WnmData) message.obj);
+ // TODO(zqiu): remove this when switch over to wificond for WNM frames
+ // monitoring.
+ mPasspointManager.receivedWnmFrame((WnmData) message.obj);
break;
case CMD_CONFIG_ND_OFFLOAD:
final boolean enabled = (message.arg1 > 0);
mWifiNative.configureNeighborDiscoveryOffload(enabled);
break;
case CMD_ENABLE_WIFI_CONNECTIVITY_MANAGER:
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.enable(message.arg1 == 1 ? true : false);
- }
+ mWifiConnectivityManager.enable(message.arg1 == 1 ? true : false);
break;
case CMD_ENABLE_AUTOJOIN_WHEN_ASSOCIATED:
final boolean allowed = (message.arg1 > 0);
- boolean old_state = mWifiConfigManager.getEnableAutoJoinWhenAssociated();
- mWifiConfigManager.setEnableAutoJoinWhenAssociated(allowed);
+ boolean old_state = mEnableAutoJoinWhenAssociated;
+ mEnableAutoJoinWhenAssociated = allowed;
if (!old_state && allowed && mScreenOn
&& getCurrentState() == mConnectedState) {
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.forceConnectivityScan();
- }
+ mWifiConnectivityManager.forceConnectivityScan();
}
break;
default:
@@ -4971,10 +4381,10 @@
}
return HANDLED;
}
+
@Override
public void exit() {
-
- mWifiLogger.stopLogging();
+ mWifiDiagnostics.stopLogging();
mIsRunning = false;
updateBatteryWorkSource(null);
@@ -4985,6 +4395,35 @@
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
mBufferedScanMsg.clear();
+
+ mNetworkInfo.setIsAvailable(false);
+ if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ mCountryCode.setReadyForChange(false);
+ }
+ }
+
+ class SupplicantStoppingState extends State {
+ @Override
+ public void enter() {
+ /* Send any reset commands to supplicant before shutting it down */
+ handleNetworkDisconnect();
+
+ String suppState = System.getProperty("init.svc.wpa_supplicant");
+ if (suppState == null) suppState = "unknown";
+
+ setWifiState(WIFI_STATE_DISABLING);
+ mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
+ logd("SupplicantStoppingState: disableSupplicant "
+ + " init.svc.wpa_supplicant=" + suppState);
+ if (mWifiNative.disableSupplicant()) {
+ mWifiNative.closeSupplicantConnection();
+ sendSupplicantConnectionChangedBroadcast(false);
+ setWifiState(WIFI_STATE_DISABLED);
+ } else {
+ // Failed to disable supplicant
+ handleSupplicantConnectionLoss(true);
+ }
+ transitionTo(mInitialState);
}
}
@@ -4996,17 +4435,17 @@
case WifiMonitor.SUP_DISCONNECTION_EVENT:
mTransitionToState = mInitialState;
break;
- case CMD_STOP_DRIVER:
- mTransitionToState = mDriverStoppingState;
- break;
case CMD_STOP_SUPPLICANT:
+ default:
mTransitionToState = mSupplicantStoppingState;
break;
- default:
- mTransitionToState = mDriverStoppingState;
- break;
}
- mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ);
+ if (p2pSendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ)) {
+ sendMessageDelayed(obtainMessage(CMD_DISABLE_P2P_WATCHDOG_TIMER,
+ mDisableP2pWatchdogCount, 0), DISABLE_P2P_GUARD_TIMER_MSEC);
+ } else {
+ transitionTo(mTransitionToState);
+ }
}
@Override
public boolean processMessage(Message message) {
@@ -5016,16 +4455,19 @@
case WifiStateMachine.CMD_DISABLE_P2P_RSP:
transitionTo(mTransitionToState);
break;
+ case WifiStateMachine.CMD_DISABLE_P2P_WATCHDOG_TIMER:
+ if (mDisableP2pWatchdogCount == message.arg1) {
+ logd("Timeout waiting for CMD_DISABLE_P2P_RSP");
+ transitionTo(mTransitionToState);
+ }
+ break;
/* Defer wifi start/shut and driver commands */
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
case CMD_START_SUPPLICANT:
case CMD_STOP_SUPPLICANT:
case CMD_START_AP:
case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
case CMD_SET_OPERATIONAL_MODE:
- case CMD_SET_FREQUENCY_BAND:
case CMD_START_SCAN:
case CMD_DISCONNECT:
case CMD_REASSOCIATE:
@@ -5040,68 +4482,12 @@
}
}
- class DriverStoppingState extends State {
- @Override
- public boolean processMessage(Message message) {
- logStateAndMessage(message, this);
-
- switch(message.what) {
- case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
- SupplicantState state = handleSupplicantStateChange(message);
- if (state == SupplicantState.INTERFACE_DISABLED) {
- transitionTo(mDriverStoppedState);
- }
- break;
- /* Queue driver commands */
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_FREQUENCY_BAND:
- case CMD_START_SCAN:
- case CMD_DISCONNECT:
- case CMD_REASSOCIATE:
- case CMD_RECONNECT:
- messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class DriverStoppedState extends State {
- @Override
- public boolean processMessage(Message message) {
- logStateAndMessage(message, this);
- switch (message.what) {
- case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
- SupplicantState state = stateChangeResult.state;
- // A WEXT bug means that we can be back to driver started state
- // unexpectedly
- if (SupplicantState.isDriverActive(state)) {
- transitionTo(mDriverStartedState);
- }
- break;
- case CMD_START_DRIVER:
- mWakeLock.acquire();
- mWifiNative.startDriver();
- mWakeLock.release();
- transitionTo(mDriverStartingState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
class ScanModeState extends State {
private int mLastOperationMode;
@Override
public void enter() {
mLastOperationMode = mOperationalMode;
+ mWifiStateTracker.updateState(WifiStateTracker.SCAN_MODE);
}
@Override
public boolean processMessage(Message message) {
@@ -5110,38 +4496,19 @@
switch(message.what) {
case CMD_SET_OPERATIONAL_MODE:
if (message.arg1 == CONNECT_MODE) {
-
- if (mLastOperationMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
- setWifiState(WIFI_STATE_ENABLED);
- // 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);
- } else {
- mWifiConfigManager.enableAllNetworks();
- }
-
- // Loose last selection choice since user toggled WiFi
- mWifiConfigManager.
- setAndEnableLastSelectedConfiguration(
- WifiConfiguration.INVALID_NETWORK_ID);
-
mOperationalMode = CONNECT_MODE;
+ setWifiState(WIFI_STATE_ENABLING);
transitionTo(mDisconnectedState);
- } else {
- // Nothing to do
- return HANDLED;
+ } else if (message.arg1 == DISABLED_MODE) {
+ transitionTo(mSupplicantStoppingState);
}
+ // Nothing to do
break;
// Handle scan. All the connection related commands are
// handled only in ConnectModeState
case CMD_START_SCAN:
handleScanRequest(message);
break;
- case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
- SupplicantState state = handleSupplicantStateChange(message);
- if (DBG) log("SupplicantState= " + state);
- break;
default:
return NOT_HANDLED;
}
@@ -5160,9 +4527,6 @@
return s;
}
switch (what) {
- case WifiMonitor.DRIVER_HUNG_EVENT:
- s = "DRIVER_HUNG_EVENT";
- break;
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
s = "AsyncChannel.CMD_CHANNEL_HALF_CONNECTED";
break;
@@ -5202,12 +4566,6 @@
case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
s = "AUTHENTICATION_FAILURE_EVENT";
break;
- case WifiMonitor.SSID_TEMP_DISABLED:
- s = "SSID_TEMP_DISABLED";
- break;
- case WifiMonitor.SSID_REENABLED:
- s = "SSID_REENABLED";
- break;
case WifiMonitor.WPS_SUCCESS_EVENT:
s = "WPS_SUCCESS_EVENT";
break;
@@ -5289,68 +4647,28 @@
void registerConnected() {
if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
- WifiConfiguration config = mWifiConfigManager.getWifiConfiguration(mLastNetworkId);
- 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.numAssociation++;
- WifiConfiguration.NetworkSelectionStatus networkSelectionStatus =
- config.getNetworkSelectionStatus();
- networkSelectionStatus.clearDisableReasonCounter();
- networkSelectionStatus.setHasEverConnected(true);
- }
+ mWifiConfigManager.updateNetworkAfterConnect(mLastNetworkId);
// On connect, reset wifiScoreReport
- mWifiScoreReport = null;
+ mWifiScoreReport.reset();
}
}
void registerDisconnected() {
if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
+ mWifiConfigManager.updateNetworkAfterDisconnect(mLastNetworkId);
// We are switching away from this configuration,
// hence record the time we were connected last
- WifiConfiguration config = mWifiConfigManager.getWifiConfiguration(mLastNetworkId);
+ WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(mLastNetworkId);
if (config != null) {
- config.lastDisconnected = System.currentTimeMillis();
- if (config.ephemeral) {
- // Remove ephemeral WifiConfigurations from file
- mWifiConfigManager.forgetNetwork(mLastNetworkId);
+ // Remove WifiConfiguration for ephemeral or Passpoint networks, since they're
+ // temporary networks.
+ if (config.ephemeral || config.isPasspoint()) {
+ mWifiConfigManager.removeNetwork(mLastNetworkId, Process.WIFI_UID);
}
}
}
}
- void noteWifiDisabledWhileAssociated() {
- // We got disabled by user while we were associated, make note of it
- int rssi = mWifiInfo.getRssi();
- WifiConfiguration config = getCurrentWifiConfiguration();
- if (getCurrentState() == mConnectedState
- && rssi != WifiInfo.INVALID_RSSI
- && config != null) {
- boolean is24GHz = mWifiInfo.is24GHz();
- boolean isBadRSSI = (is24GHz && rssi < mWifiConfigManager.mThresholdMinimumRssi24.get())
- || (!is24GHz && rssi < mWifiConfigManager.mThresholdMinimumRssi5.get());
- boolean isLowRSSI =
- (is24GHz && rssi < mWifiConfigManager.mThresholdQualifiedRssi24.get())
- || (!is24GHz && mWifiInfo.getRssi() <
- mWifiConfigManager.mThresholdQualifiedRssi5.get());
- boolean isHighRSSI = (is24GHz && rssi
- >= mWifiConfigManager.mThresholdSaturatedRssi24.get())
- || (!is24GHz && mWifiInfo.getRssi()
- >= mWifiConfigManager.mThresholdSaturatedRssi5.get());
- if (isBadRSSI) {
- // Take note that we got disabled while RSSI was Bad
- config.numUserTriggeredWifiDisableLowRSSI++;
- } else if (isLowRSSI) {
- // Take note that we got disabled while RSSI was Low
- config.numUserTriggeredWifiDisableBadRSSI++;
- } else if (!isHighRSSI) {
- // Take note that we got disabled while RSSI was Not high
- config.numUserTriggeredWifiDisableNotHighRSSI++;
- }
- }
- }
-
/**
* Returns Wificonfiguration object correponding to the currently connected network, null if
* not connected.
@@ -5359,7 +4677,7 @@
if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) {
return null;
}
- return mWifiConfigManager.getWifiConfiguration(mLastNetworkId);
+ return mWifiConfigManager.getConfiguredNetwork(mLastNetworkId);
}
ScanResult getCurrentScanResult() {
@@ -5372,7 +4690,7 @@
BSSID = mTargetRoamBSSID;
}
ScanDetailCache scanDetailCache =
- mWifiConfigManager.getScanDetailCache(config);
+ mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
if (scanDetailCache == null) {
return null;
@@ -5382,7 +4700,7 @@
}
String getCurrentBSSID() {
- if (linkDebouncing) {
+ if (isLinkDebouncing()) {
return null;
}
return mLastBssid;
@@ -5392,7 +4710,12 @@
@Override
public void enter() {
- // Let the system know that wifi is enabled
+ if (!mWifiNative.removeAllNetworks()) {
+ loge("Failed to remove networks on entering connect mode");
+ }
+ mWifiInfo.reset();
+ mWifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
+ // Let the system know that wifi is available in client mode.
setWifiState(WIFI_STATE_ENABLED);
mNetworkInfo.setIsAvailable(true);
@@ -5402,26 +4725,29 @@
setNetworkDetailedState(DetailedState.DISCONNECTED);
// Inform WifiConnectivityManager that Wifi is enabled
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.setWifiEnabled(true);
- }
+ mWifiConnectivityManager.setWifiEnabled(true);
// Inform metrics that Wifi is Enabled (but not yet connected)
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED);
+ // Inform p2p service that wifi is up and ready when applicable
+ p2pSendMessage(WifiStateMachine.CMD_ENABLE_P2P);
}
@Override
public void exit() {
// Let the system know that wifi is not available since we are exiting client mode.
- setWifiState(WIFI_STATE_DISABLED);
mNetworkInfo.setIsAvailable(false);
if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
// Inform WifiConnectivityManager that Wifi is disabled
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.setWifiEnabled(false);
- }
+ mWifiConnectivityManager.setWifiEnabled(false);
// Inform metrics that Wifi is being disabled (Toggled, airplane enabled, etc)
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_DISABLED);
+
+ if (!mWifiNative.removeAllNetworks()) {
+ loge("Failed to remove networks on exiting connect mode");
+ }
+ mWifiInfo.reset();
+ mWifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
}
@Override
@@ -5434,73 +4760,57 @@
String ssid;
NetworkUpdateResult result;
Set<Integer> removedNetworkIds;
+ int reasonCode;
+ boolean timedOut;
logStateAndMessage(message, this);
switch (message.what) {
case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_ASSOC_FAILURE);
+ mWifiDiagnostics.captureBugReportData(
+ WifiDiagnostics.REPORT_REASON_ASSOC_FAILURE);
didBlackListBSSID = false;
bssid = (String) message.obj;
+ timedOut = message.arg1 > 0;
+ reasonCode = message.arg2;
+ Log.d(TAG, "Assocation Rejection event: bssid=" + bssid + " reason code="
+ + reasonCode + " timedOut=" + Boolean.toString(timedOut));
if (bssid == null || TextUtils.isEmpty(bssid)) {
// If BSSID is null, use the target roam BSSID
bssid = mTargetRoamBSSID;
}
if (bssid != null) {
// If we have a BSSID, tell configStore to black list it
- if (mWifiConnectivityManager != null) {
- didBlackListBSSID = mWifiConnectivityManager.trackBssid(bssid,
- false);
- }
+ didBlackListBSSID = mWifiConnectivityManager.trackBssid(bssid, false,
+ reasonCode);
}
-
mWifiConfigManager.updateNetworkSelectionStatus(mTargetNetworkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_ASSOCIATION_REJECTION);
-
mSupplicantStateTracker.sendMessage(WifiMonitor.ASSOCIATION_REJECTION_EVENT);
//If rejection occurred while Metrics is tracking a ConnnectionEvent, end it.
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);
+ mWifiDiagnostics.captureBugReportData(
+ WifiDiagnostics.REPORT_REASON_AUTH_FAILURE);
mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
- if (mTargetNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
- mWifiConfigManager.updateNetworkSelectionStatus(mTargetNetworkId,
- WifiConfiguration.NetworkSelectionStatus
- .DISABLED_AUTHENTICATION_FAILURE);
- }
+ mWifiConfigManager.updateNetworkSelectionStatus(mTargetNetworkId,
+ WifiConfiguration.NetworkSelectionStatus
+ .DISABLED_AUTHENTICATION_FAILURE);
//If failure occurred while Metrics is tracking a ConnnectionEvent, end it.
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
- mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(),
- mTargetRoamBSSID,
- WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
- break;
- case WifiMonitor.SSID_TEMP_DISABLED:
- Log.e(TAG, "Supplicant SSID temporary disabled:"
- + mWifiConfigManager.getWifiConfiguration(message.arg1));
- mWifiConfigManager.updateNetworkSelectionStatus(
- message.arg1,
- WifiConfiguration.NetworkSelectionStatus
- .DISABLED_AUTHENTICATION_FAILURE);
- reportConnectionAttemptEnd(
- WifiMetrics.ConnectionEvent.FAILURE_SSID_TEMP_DISABLED,
- WifiMetricsProto.ConnectionEvent.HLF_NONE);
- mWifiLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(getTargetSsid(),
- mTargetRoamBSSID,
- WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
- break;
- case WifiMonitor.SSID_REENABLED:
- Log.d(TAG, "Supplicant SSID reenable:"
- + mWifiConfigManager.getWifiConfiguration(message.arg1));
- // Do not re-enable it in Quality Network Selection since framework has its own
- // Algorithm of disable/enable
+ mWifiInjector.getWifiLastResortWatchdog()
+ .noteConnectionFailureAndTriggerIfNeeded(
+ getTargetSsid(), mTargetRoamBSSID,
+ WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
break;
case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
SupplicantState state = handleSupplicantStateChange(message);
@@ -5511,8 +4821,10 @@
handleNetworkDisconnect();
}
log("Detected an interface down, restart driver");
- transitionTo(mDriverStoppedState);
- sendMessage(CMD_START_DRIVER);
+ // Rely on the fact that this will force us into killing supplicant and then
+ // restart supplicant from a clean state.
+ transitionTo(mSupplicantStoppingState);
+ sendMessage(CMD_START_SUPPLICANT);
break;
}
@@ -5521,9 +4833,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);
}
@@ -5537,6 +4851,8 @@
break;
case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
if (message.arg1 == 1) {
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
+ StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST);
mWifiNative.disconnect();
mTemporarilyDisconnectWifi = true;
} else {
@@ -5544,144 +4860,44 @@
mTemporarilyDisconnectWifi = false;
}
break;
- case CMD_ADD_OR_UPDATE_NETWORK:
- // Only the current foreground user can modify networks.
- if (!mWifiConfigManager.isCurrentUserProfile(
- UserHandle.getUserId(message.sendingUid))) {
- loge("Only the current foreground user can modify networks "
- + " currentUserId=" + mWifiConfigManager.getCurrentUserId()
- + " sendingUserId=" + UserHandle.getUserId(message.sendingUid));
- replyToMessage(message, message.what, FAILURE);
- break;
- }
-
- config = (WifiConfiguration) message.obj;
-
- if (!recordUidIfAuthorized(config, message.sendingUid,
- /* onlyAnnotate */ false)) {
- logw("Not authorized to update network "
- + " config=" + config.SSID
- + " cnid=" + config.networkId
- + " uid=" + message.sendingUid);
- replyToMessage(message, message.what, FAILURE);
- break;
- }
-
- int res = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
- if (res < 0) {
- messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
- } else {
- WifiConfiguration curConfig = getCurrentWifiConfiguration();
- if (curConfig != null && config != null) {
- WifiConfiguration.NetworkSelectionStatus networkStatus =
- config.getNetworkSelectionStatus();
- if (curConfig.priority < config.priority && networkStatus != null
- && !networkStatus.isNetworkPermanentlyDisabled()) {
- // Interpret this as a connect attempt
- // Set the last selected configuration so as to allow the system to
- // stick the last user choice without persisting the choice
- mWifiConfigManager.setAndEnableLastSelectedConfiguration(res);
- mWifiConfigManager.updateLastConnectUid(config, message.sendingUid);
- boolean persist = mWifiConfigManager
- .checkConfigOverridePermission(message.sendingUid);
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.connectToUserSelectNetwork(res,
- persist);
- }
-
- // Remember time of last connection attempt
- lastConnectAttemptTimestamp = System.currentTimeMillis();
- mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
-
- // As a courtesy to the caller, trigger a scan now
- startScan(ADD_OR_UPDATE_SOURCE, 0, null, WIFI_WORK_SOURCE);
- }
- }
- }
- replyToMessage(message, CMD_ADD_OR_UPDATE_NETWORK, res);
- break;
case CMD_REMOVE_NETWORK:
- // Only the current foreground user can modify networks.
- if (!mWifiConfigManager.isCurrentUserProfile(
- UserHandle.getUserId(message.sendingUid))) {
- loge("Only the current foreground user can modify networks "
- + " currentUserId=" + mWifiConfigManager.getCurrentUserId()
- + " sendingUserId=" + UserHandle.getUserId(message.sendingUid));
- replyToMessage(message, message.what, FAILURE);
- break;
- }
- netId = message.arg1;
-
- if (!mWifiConfigManager.canModifyNetwork(message.sendingUid, netId,
- /* onlyAnnotate */ false)) {
- logw("Not authorized to remove network "
- + " cnid=" + netId
- + " uid=" + message.sendingUid);
- replyToMessage(message, message.what, FAILURE);
- break;
- }
-
- ok = mWifiConfigManager.removeNetwork(message.arg1);
- if (!ok) {
+ if (!deleteNetworkConfigAndSendReply(message, false)) {
+ // failed to remove the config and caller was notified
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+ break;
}
- replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
+ // we successfully deleted the network config
+ netId = message.arg1;
+ if (netId == mTargetNetworkId || netId == mLastNetworkId) {
+ // Disconnect and let autojoin reselect a new network
+ sendMessage(CMD_DISCONNECT);
+ }
break;
case CMD_ENABLE_NETWORK:
- // Only the current foreground user can modify networks.
- if (!mWifiConfigManager.isCurrentUserProfile(
- UserHandle.getUserId(message.sendingUid))) {
- loge("Only the current foreground user can modify networks "
- + " currentUserId=" + mWifiConfigManager.getCurrentUserId()
- + " sendingUserId=" + UserHandle.getUserId(message.sendingUid));
- replyToMessage(message, message.what, FAILURE);
- break;
- }
-
boolean disableOthers = message.arg2 == 1;
netId = message.arg1;
- config = mWifiConfigManager.getWifiConfiguration(netId);
- if (config == null) {
- loge("No network with id = " + netId);
- messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
- replyToMessage(message, message.what, FAILURE);
- break;
- }
- // disable other only means select this network, does not mean all other
- // networks need to be disabled
if (disableOthers) {
- // Remember time of last connection attempt
- lastConnectAttemptTimestamp = System.currentTimeMillis();
- mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
+ // If the app has all the necessary permissions, this will trigger a connect
+ // attempt.
+ ok = connectToUserSelectNetwork(netId, message.sendingUid, false);
+ } else {
+ ok = mWifiConfigManager.enableNetwork(netId, false, message.sendingUid);
}
- // Cancel auto roam requests
- autoRoamSetBSSID(netId, "any");
-
- ok = mWifiConfigManager.enableNetwork(
- config, disableOthers, message.sendingUid);
if (!ok) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
- } else {
- if (disableOthers) {
- mTargetNetworkId = netId;
- }
- mWifiConnectivityManager.forceConnectivityScan();
}
-
replyToMessage(message, message.what, ok ? SUCCESS : FAILURE);
break;
- case CMD_ENABLE_ALL_NETWORKS:
- long time = android.os.SystemClock.elapsedRealtime();
- if (time - mLastEnableAllNetworksTime > MIN_INTERVAL_ENABLE_ALL_NETWORKS_MS) {
- mWifiConfigManager.enableAllNetworks();
- mLastEnableAllNetworksTime = time;
- }
- break;
case WifiManager.DISABLE_NETWORK:
- if (mWifiConfigManager.updateNetworkSelectionStatus(message.arg1,
- WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER)) {
+ netId = message.arg1;
+ if (mWifiConfigManager.disableNetwork(netId, message.sendingUid)) {
replyToMessage(message, WifiManager.DISABLE_NETWORK_SUCCEEDED);
+ if (netId == mTargetNetworkId || netId == mLastNetworkId) {
+ // Disconnect and let autojoin reselect a new network
+ sendMessage(CMD_DISCONNECT);
+ }
} else {
+ loge("Failed to disable network");
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.DISABLE_NETWORK_FAILED,
WifiManager.ERROR);
@@ -5690,57 +4906,35 @@
case CMD_DISABLE_EPHEMERAL_NETWORK:
config = mWifiConfigManager.disableEphemeralNetwork((String)message.obj);
if (config != null) {
- if (config.networkId == mLastNetworkId) {
+ if (config.networkId == mTargetNetworkId
+ || config.networkId == mLastNetworkId) {
// Disconnect and let autojoin reselect a new network
sendMessage(CMD_DISCONNECT);
}
}
break;
- case CMD_BLACKLIST_NETWORK:
- mWifiConfigManager.blackListBssid((String) message.obj);
- break;
- case CMD_CLEAR_BLACKLIST:
- mWifiConfigManager.clearBssidBlacklist();
- break;
case CMD_SAVE_CONFIG:
- ok = mWifiConfigManager.saveConfig();
-
- if (DBG) logd("did save config " + ok);
+ ok = mWifiConfigManager.saveToStore(true);
replyToMessage(message, CMD_SAVE_CONFIG, ok ? SUCCESS : FAILURE);
-
// Inform the backup manager about a data change
mBackupManagerProxy.notifyDataChanged();
break;
- case CMD_GET_CONFIGURED_NETWORKS:
- replyToMessage(message, message.what,
- mWifiConfigManager.getSavedNetworks());
- break;
- case CMD_HAS_CARRIER_CONFIGURED_NETWORKS:
- replyToMessage(message, message.what,
- (Boolean)mWifiConfigManager.hasCarrierNetworks());
- break;
case WifiMonitor.SUP_REQUEST_IDENTITY:
- int networkId = message.arg2;
+ int supplicantNetworkId = message.arg2;
+ netId = lookupFrameworkNetworkId(supplicantNetworkId);
boolean identitySent = false;
- int eapMethod = WifiEnterpriseConfig.Eap.NONE;
-
- if (targetWificonfiguration != null
- && targetWificonfiguration.enterpriseConfig != null) {
- eapMethod = targetWificonfiguration.enterpriseConfig.getEapMethod();
- }
-
// For SIM & AKA/AKA' EAP method Only, get identity from ICC
if (targetWificonfiguration != null
- && targetWificonfiguration.networkId == networkId
- && (targetWificonfiguration.allowedKeyManagement
- .get(WifiConfiguration.KeyMgmt.IEEE8021X)
- || targetWificonfiguration.allowedKeyManagement
- .get(WifiConfiguration.KeyMgmt.WPA_EAP))
- && TelephonyUtil.isSimEapMethod(eapMethod)) {
- String identity = TelephonyUtil.getSimIdentity(mContext, eapMethod);
+ && targetWificonfiguration.networkId == netId
+ && TelephonyUtil.isSimConfig(targetWificonfiguration)) {
+ String identity =
+ TelephonyUtil.getSimIdentity(getTelephonyManager(),
+ targetWificonfiguration);
if (identity != null) {
- mWifiNative.simIdentityResponse(networkId, identity);
+ mWifiNative.simIdentityResponse(supplicantNetworkId, identity);
identitySent = true;
+ } else {
+ Log.e(TAG, "Unable to retrieve identity from Telephony");
}
}
if (!identitySent) {
@@ -5749,14 +4943,13 @@
if (targetWificonfiguration != null && ssid != null
&& targetWificonfiguration.SSID != null
&& targetWificonfiguration.SSID.equals("\"" + ssid + "\"")) {
- mWifiConfigManager.updateNetworkSelectionStatus(targetWificonfiguration,
+ mWifiConfigManager.updateNetworkSelectionStatus(
+ targetWificonfiguration.networkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_AUTHENTICATION_NO_CREDENTIALS);
}
- // Disconnect now, as we don't have any way to fullfill
- // the supplicant request.
- mWifiConfigManager.setAndEnableLastSelectedConfiguration(
- WifiConfiguration.INVALID_NETWORK_ID);
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
+ StaEvent.DISCONNECT_GENERIC);
mWifiNative.disconnect();
}
break;
@@ -5774,158 +4967,64 @@
loge("Invalid sim auth request");
}
break;
- case CMD_GET_PRIVILEGED_CONFIGURED_NETWORKS:
- replyToMessage(message, message.what,
- mWifiConfigManager.getPrivilegedSavedNetworks());
- break;
case CMD_GET_MATCHING_CONFIG:
replyToMessage(message, message.what,
- mWifiConfigManager.getMatchingConfig((ScanResult)message.obj));
+ mPasspointManager.getMatchingWifiConfig((ScanResult) message.obj));
break;
case CMD_RECONNECT:
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.forceConnectivityScan();
- }
+ mWifiConnectivityManager.forceConnectivityScan();
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;
- case CMD_AUTO_ROAM:
+ case CMD_START_ROAM:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
return HANDLED;
- case CMD_AUTO_CONNECT:
- /* Work Around: wpa_supplicant can get in a bad state where it returns a non
- * associated status to the STATUS command but somehow-someplace still thinks
- * it is associated and thus will ignore select/reconnect command with
- * following message:
- * "Already associated with the selected network - do nothing"
- *
- * Hence, sends a disconnect to supplicant first.
- */
- didDisconnect = false;
- if (getCurrentState() != mDisconnectedState) {
- /** Supplicant will ignore the reconnect if we are currently associated,
- * hence trigger a disconnect
- */
- didDisconnect = true;
- mWifiNative.disconnect();
- }
-
+ case CMD_START_CONNECT:
/* connect command coming from auto-join */
netId = message.arg1;
- mTargetNetworkId = netId;
- mTargetRoamBSSID = (String) message.obj;
- config = mWifiConfigManager.getWifiConfiguration(netId);
- logd("CMD_AUTO_CONNECT sup state "
+ bssid = (String) message.obj;
+ config = mWifiConfigManager.getConfiguredNetworkWithPassword(netId);
+ logd("CMD_START_CONNECT sup state "
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName()
+ " nid=" + Integer.toString(netId)
- + " roam=" + Boolean.toString(mAutoRoaming));
+ + " roam=" + Boolean.toString(mIsAutoRoaming));
if (config == null) {
- loge("AUTO_CONNECT and no config, bail out...");
+ loge("CMD_START_CONNECT and no config, bail out...");
break;
}
+ mTargetNetworkId = netId;
+ setTargetBssid(config, bssid);
- /* Make sure we cancel any previous roam request */
- setTargetBssid(config, mTargetRoamBSSID);
-
- /* Save the network config */
- logd("CMD_AUTO_CONNECT will save config -> " + config.SSID
- + " nid=" + Integer.toString(netId));
- result = mWifiConfigManager.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
- netId = result.getNetworkId();
- logd("CMD_AUTO_CONNECT did save config -> "
- + " nid=" + Integer.toString(netId));
-
- // Since we updated the config,read it back from config store:
- config = mWifiConfigManager.getWifiConfiguration(netId);
- if (config == null) {
- loge("CMD_AUTO_CONNECT couldn't update the config, got null config");
- break;
- }
- if (netId != config.networkId) {
- loge("CMD_AUTO_CONNECT couldn't update the config, want"
- + " nid=" + Integer.toString(netId) + " but got" + config.networkId);
- break;
- }
-
- if (deferForUserInput(message, netId, false)) {
- break;
- } else if (mWifiConfigManager.getWifiConfiguration(netId).userApproved ==
- WifiConfiguration.USER_BANNED) {
- replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
- WifiManager.NOT_AUTHORIZED);
- break;
- }
-
- // If we're autojoining a network that the user or an app explicitly selected,
- // keep track of the UID that selected it.
- // TODO(b/26786318): Keep track of the lastSelectedConfiguration and the
- // lastConnectUid on a per-user basis.
- int lastConnectUid = WifiConfiguration.UNKNOWN_UID;
-
- //Start a new ConnectionEvent due to auto_connect, assume we are connecting
- //between different networks due to QNS, setting ROAM_UNRELATED
- mWifiMetrics.startConnectionEvent(config, mTargetRoamBSSID,
+ reportConnectionAttemptStart(config, mTargetRoamBSSID,
WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED);
- if (!didDisconnect) {
- //If we were originally disconnected, then this was not any kind of ROAM
- mWifiMetrics.setConnectionEventRoamType(
- WifiMetricsProto.ConnectionEvent.ROAM_NONE);
- }
- //Determine if this CONNECTION is for a user selection
- if (mWifiConfigManager.isLastSelectedConfiguration(config)
- && mWifiConfigManager.isCurrentUserProfile(
- UserHandle.getUserId(config.lastConnectUid))) {
- lastConnectUid = config.lastConnectUid;
- mWifiMetrics.setConnectionEventRoamType(
- WifiMetricsProto.ConnectionEvent.ROAM_USER_SELECTED);
- }
- if (mWifiConfigManager.selectNetwork(config, /* updatePriorities = */ false,
- lastConnectUid) && mWifiNative.reconnect()) {
- lastConnectAttemptTimestamp = System.currentTimeMillis();
- targetWificonfiguration = mWifiConfigManager.getWifiConfiguration(netId);
- config = mWifiConfigManager.getWifiConfiguration(netId);
- if (config != null
- && !mWifiConfigManager.isLastSelectedConfiguration(config)) {
- // If we autojoined a different config than the user selected one,
- // it means we could not see the last user selection,
- // or that the last user selection was faulty and ended up blacklisted
- // for some reason (in which case the user is notified with an error
- // message in the Wifi picker), and thus we managed to auto-join away
- // from the selected config. -> in that case we need to forget
- // the selection because we don't want to abruptly switch back to it.
- //
- // Note that the user selection is also forgotten after a period of time
- // during which the device has been disconnected.
- // The default value is 30 minutes : see the code path at bottom of
- // setScanResults() function.
- mWifiConfigManager.
- setAndEnableLastSelectedConfiguration(
- WifiConfiguration.INVALID_NETWORK_ID);
- }
- mAutoRoaming = false;
- if (isRoaming() || linkDebouncing) {
+ if (mWifiNative.connectToNetwork(config)) {
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config);
+ lastConnectAttemptTimestamp = mClock.getWallClockMillis();
+ targetWificonfiguration = config;
+ mIsAutoRoaming = false;
+ if (isLinkDebouncing()) {
transitionTo(mRoamingState);
- } else if (didDisconnect) {
+ } else if (getCurrentState() != mDisconnectedState) {
transitionTo(mDisconnectingState);
}
} else {
- loge("Failed to connect config: " + config + " netId: " + netId);
- replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
- WifiManager.ERROR);
+ loge("CMD_START_CONNECT Failed to start connection to network " + config);
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
+ WifiManager.ERROR);
break;
}
break;
@@ -5948,21 +5047,8 @@
}
break;
case WifiManager.CONNECT_NETWORK:
- // Only the current foreground user and System UI (which runs as user 0 but acts
- // on behalf of the current foreground user) can modify networks.
- if (!mWifiConfigManager.isCurrentUserProfile(
- UserHandle.getUserId(message.sendingUid)) &&
- message.sendingUid != mSystemUiUid) {
- loge("Only the current foreground user can modify networks "
- + " currentUserId=" + mWifiConfigManager.getCurrentUserId()
- + " sendingUserId=" + UserHandle.getUserId(message.sendingUid));
- replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
- WifiManager.NOT_AUTHORIZED);
- break;
- }
-
/**
- * The connect message can contain a network id passed as arg1 on message or
+ * The connect message can contain a network id passed as arg1 on message or
* or a config passed as obj on message.
* For a new network, a config is passed to create and connect.
* For an existing network, a network id is passed
@@ -5970,153 +5056,36 @@
netId = message.arg1;
config = (WifiConfiguration) message.obj;
mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
- boolean updatedExisting = false;
-
- /* Save the network config */
+ boolean hasCredentialChanged = false;
+ // New network addition.
if (config != null) {
- // When connecting to an access point, WifiStateMachine wants to update the
- // relevant config with administrative data. This update should not be
- // considered a 'real' update, therefore lockdown by Device Owner must be
- // disregarded.
- if (!recordUidIfAuthorized(config, message.sendingUid,
- /* onlyAnnotate */ true)) {
- logw("Not authorized to update network "
- + " config=" + config.SSID
- + " cnid=" + config.networkId
- + " uid=" + message.sendingUid);
+ result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
+ if (!result.isSuccess()) {
+ loge("CONNECT_NETWORK adding/updating config=" + config + " failed");
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
- WifiManager.NOT_AUTHORIZED);
+ WifiManager.ERROR);
break;
}
- String configKey = config.configKey(true /* allowCached */);
- WifiConfiguration savedConfig =
- mWifiConfigManager.getWifiConfiguration(configKey);
- if (savedConfig != null) {
- // There is an existing config with this netId, but it wasn't exposed
- // (either AUTO_JOIN_DELETED or ephemeral; see WifiConfigManager#
- // getConfiguredNetworks). Remove those bits and update the config.
- config = savedConfig;
- logd("CONNECT_NETWORK updating existing config with id=" +
- config.networkId + " configKey=" + configKey);
- config.ephemeral = false;
- mWifiConfigManager.updateNetworkSelectionStatus(config,
- WifiConfiguration.NetworkSelectionStatus
- .NETWORK_SELECTION_ENABLE);
- updatedExisting = true;
- }
-
- result = mWifiConfigManager.saveNetwork(config, message.sendingUid);
netId = result.getNetworkId();
+ hasCredentialChanged = result.hasCredentialChanged();
}
- config = mWifiConfigManager.getWifiConfiguration(netId);
- if (config == null) {
- logd("CONNECT_NETWORK no config for id=" + Integer.toString(netId) + " "
- + mSupplicantStateTracker.getSupplicantStateName() + " my state "
- + getCurrentState().getName());
- replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
- WifiManager.ERROR);
- break;
- }
- mTargetNetworkId = netId;
- autoRoamSetBSSID(netId, "any");
- if (message.sendingUid == Process.WIFI_UID
- || message.sendingUid == Process.SYSTEM_UID) {
- // As a sanity measure, clear the BSSID in the supplicant network block.
- // If system or Wifi Settings want to connect, they will not
- // specify the BSSID.
- // If an app however had added a BSSID to this configuration, and the BSSID
- // was wrong, Then we would forever fail to connect until that BSSID
- // is cleaned up.
- clearConfigBSSID(config, "CONNECT_NETWORK");
- }
-
- if (deferForUserInput(message, netId, true)) {
- break;
- } else if (mWifiConfigManager.getWifiConfiguration(netId).userApproved ==
- WifiConfiguration.USER_BANNED) {
+ if (!connectToUserSelectNetwork(
+ netId, message.sendingUid, hasCredentialChanged)) {
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.NOT_AUTHORIZED);
break;
}
-
- mAutoRoaming = false;
-
- /* Tell network selection the user did try to connect to that network if from
- settings */
- boolean persist =
- mWifiConfigManager.checkConfigOverridePermission(message.sendingUid);
-
-
- mWifiConfigManager.setAndEnableLastSelectedConfiguration(netId);
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.connectToUserSelectNetwork(netId, persist);
- }
- didDisconnect = false;
- if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
- && mLastNetworkId != netId) {
- /** Supplicant will ignore the reconnect if we are currently associated,
- * hence trigger a disconnect
- */
- didDisconnect = true;
- mWifiNative.disconnect();
- }
-
- //Start a new ConnectionEvent due to connect_network, this is always user
- //selected
- mWifiMetrics.startConnectionEvent(config, mTargetRoamBSSID,
- WifiMetricsProto.ConnectionEvent.ROAM_USER_SELECTED);
- if (mWifiConfigManager.selectNetwork(config, /* updatePriorities = */ true,
- message.sendingUid) && mWifiNative.reconnect()) {
- lastConnectAttemptTimestamp = System.currentTimeMillis();
- targetWificonfiguration = mWifiConfigManager.getWifiConfiguration(netId);
-
- /* The state tracker handles enabling networks upon completion/failure */
- mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
- replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
- if (didDisconnect) {
- /* Expect a disconnection from the old connection */
- transitionTo(mDisconnectingState);
- } else if (updatedExisting && getCurrentState() == mConnectedState &&
- getCurrentWifiConfiguration().networkId == netId) {
- // Update the current set of network capabilities, but stay in the
- // current state.
- updateCapabilities(config);
- } else {
- /**
- * Directly go to disconnected state where we
- * process the connection events from supplicant
- */
- transitionTo(mDisconnectedState);
- }
- } else {
- loge("Failed to connect config: " + config + " netId: " + netId);
- replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
- WifiManager.ERROR);
- reportConnectionAttemptEnd(
- WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
- WifiMetricsProto.ConnectionEvent.HLF_NONE);
- break;
- }
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_CONNECT_NETWORK, config);
+ broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
break;
case WifiManager.SAVE_NETWORK:
- mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
- // Fall thru
- case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
- // Only the current foreground user can modify networks.
- if (!mWifiConfigManager.isCurrentUserProfile(
- UserHandle.getUserId(message.sendingUid))) {
- loge("Only the current foreground user can modify networks "
- + " currentUserId=" + mWifiConfigManager.getCurrentUserId()
- + " sendingUserId=" + UserHandle.getUserId(message.sendingUid));
- replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
- WifiManager.NOT_AUTHORIZED);
- break;
- }
-
- lastSavedConfigurationAttempt = null; // Used for debug
config = (WifiConfiguration) message.obj;
+ mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
if (config == null) {
- loge("ERROR: SAVE_NETWORK with null configuration"
+ loge("SAVE_NETWORK with null configuration"
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName());
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
@@ -6124,144 +5093,106 @@
WifiManager.ERROR);
break;
}
- lastSavedConfigurationAttempt = new WifiConfiguration(config);
- int nid = config.networkId;
- logd("SAVE_NETWORK id=" + Integer.toString(nid)
- + " config=" + config.SSID
- + " nid=" + config.networkId
- + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
- + " my state " + getCurrentState().getName());
-
- // Only record the uid if this is user initiated
- boolean checkUid = (message.what == WifiManager.SAVE_NETWORK);
- if (checkUid && !recordUidIfAuthorized(config, message.sendingUid,
- /* onlyAnnotate */ false)) {
- logw("Not authorized to update network "
- + " config=" + config.SSID
- + " cnid=" + config.networkId
- + " uid=" + message.sendingUid);
- replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
- WifiManager.NOT_AUTHORIZED);
- break;
- }
-
- result = mWifiConfigManager.saveNetwork(config, WifiConfiguration.UNKNOWN_UID);
- if (result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID) {
- if (mWifiInfo.getNetworkId() == result.getNetworkId()) {
- if (result.hasIpChanged()) {
- // The currently connection configuration was changed
- // We switched from DHCP to static or from static to DHCP, or the
- // static IP address has changed.
- log("Reconfiguring IP on connection");
- // TODO: clear addresses and disable IPv6
- // to simplify obtainingIpState.
- transitionTo(mObtainingIpState);
- }
- if (result.hasProxyChanged()) {
- log("Reconfiguring proxy on connection");
- mIpManager.setHttpProxy(
- mWifiConfigManager.getProxyProperties(mLastNetworkId));
- }
- }
- replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
- broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
-
- if (DBG) {
- logd("Success save network nid="
- + Integer.toString(result.getNetworkId()));
- }
-
- /**
- * If the command comes from WifiManager, then
- * tell autojoin the user did try to modify and save that network,
- * and interpret the SAVE_NETWORK as a request to connect
- */
- boolean user = message.what == WifiManager.SAVE_NETWORK;
-
- // Did this connect come from settings
- boolean persistConnect =
- mWifiConfigManager.checkConfigOverridePermission(
- message.sendingUid);
-
- if (user) {
- mWifiConfigManager.updateLastConnectUid(config, message.sendingUid);
- mWifiConfigManager.writeKnownNetworkHistory();
- }
-
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.connectToUserSelectNetwork(
- result.getNetworkId(), persistConnect);
- }
- } else {
- loge("Failed to save network");
+ result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
+ if (!result.isSuccess()) {
+ loge("SAVE_NETWORK adding/updating config=" + config + " failed");
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
WifiManager.ERROR);
+ break;
}
+ if (!mWifiConfigManager.enableNetwork(
+ result.getNetworkId(), false, message.sendingUid)) {
+ loge("SAVE_NETWORK enabling config=" + config + " failed");
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+ replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED,
+ WifiManager.ERROR);
+ break;
+ }
+ netId = result.getNetworkId();
+ if (mWifiInfo.getNetworkId() == netId) {
+ if (result.hasCredentialChanged()) {
+ // The network credentials changed and we're connected to this network,
+ // start a new connection with the updated credentials.
+ logi("SAVE_NETWORK credential changed for config=" + config.configKey()
+ + ", Reconnecting.");
+ startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY);
+ } else {
+ if (result.hasProxyChanged()) {
+ log("Reconfiguring proxy on connection");
+ mIpManager.setHttpProxy(
+ getCurrentWifiConfiguration().getHttpProxy());
+ }
+ if (result.hasIpChanged()) {
+ // The current connection configuration was changed
+ // We switched from DHCP to static or from static to DHCP, or the
+ // static IP address has changed.
+ log("Reconfiguring IP on connection");
+ // TODO(b/36576642): clear addresses and disable IPv6
+ // to simplify obtainingIpState.
+ transitionTo(mObtainingIpState);
+ }
+ }
+ }
+ broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
+ replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
break;
case WifiManager.FORGET_NETWORK:
- // Only the current foreground user can modify networks.
- if (!mWifiConfigManager.isCurrentUserProfile(
- UserHandle.getUserId(message.sendingUid))) {
- loge("Only the current foreground user can modify networks "
- + " currentUserId=" + mWifiConfigManager.getCurrentUserId()
- + " sendingUserId=" + UserHandle.getUserId(message.sendingUid));
- replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
- WifiManager.NOT_AUTHORIZED);
+ if (!deleteNetworkConfigAndSendReply(message, true)) {
+ // Caller was notified of failure, nothing else to do
break;
}
-
- // Debug only, remember last configuration that was forgotten
- WifiConfiguration toRemove
- = mWifiConfigManager.getWifiConfiguration(message.arg1);
- if (toRemove == null) {
- lastForgetConfigurationAttempt = null;
- } else {
- lastForgetConfigurationAttempt = new WifiConfiguration(toRemove);
- }
- // check that the caller owns this network
+ // the network was deleted
netId = message.arg1;
-
- if (!mWifiConfigManager.canModifyNetwork(message.sendingUid, netId,
- /* onlyAnnotate */ false)) {
- logw("Not authorized to forget network "
- + " cnid=" + netId
- + " uid=" + message.sendingUid);
- replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
- WifiManager.NOT_AUTHORIZED);
- break;
- }
-
- if (mWifiConfigManager.forgetNetwork(message.arg1)) {
- replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
- broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_FORGOT,
- (WifiConfiguration) message.obj);
- } else {
- loge("Failed to forget network");
- replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
- WifiManager.ERROR);
+ if (netId == mTargetNetworkId || netId == mLastNetworkId) {
+ // Disconnect and let autojoin reselect a new network
+ sendMessage(CMD_DISCONNECT);
}
break;
case WifiManager.START_WPS:
WpsInfo wpsInfo = (WpsInfo) message.obj;
- WpsResult wpsResult;
+ if (wpsInfo == null) {
+ loge("Cannot start WPS with null WpsInfo object");
+ replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
+ break;
+ }
+ WpsResult wpsResult = new WpsResult();
+ // TODO(b/32898136): Not needed when we start deleting networks from supplicant
+ // on disconnect.
+ if (!mWifiNative.removeAllNetworks()) {
+ loge("Failed to remove networks before WPS");
+ }
switch (wpsInfo.setup) {
case WpsInfo.PBC:
- wpsResult = mWifiConfigManager.startWpsPbc(wpsInfo);
+ if (mWifiNative.startWpsPbc(wpsInfo.BSSID)) {
+ wpsResult.status = WpsResult.Status.SUCCESS;
+ } else {
+ Log.e(TAG, "Failed to start WPS push button configuration");
+ wpsResult.status = WpsResult.Status.FAILURE;
+ }
break;
case WpsInfo.KEYPAD:
- wpsResult = mWifiConfigManager.startWpsWithPinFromAccessPoint(wpsInfo);
+ if (mWifiNative.startWpsRegistrar(wpsInfo.BSSID, wpsInfo.pin)) {
+ wpsResult.status = WpsResult.Status.SUCCESS;
+ } else {
+ Log.e(TAG, "Failed to start WPS push button configuration");
+ wpsResult.status = WpsResult.Status.FAILURE;
+ }
break;
case WpsInfo.DISPLAY:
- wpsResult = mWifiConfigManager.startWpsWithPinFromDevice(wpsInfo);
+ wpsResult.pin = mWifiNative.startWpsPinDisplay(wpsInfo.BSSID);
+ if (!TextUtils.isEmpty(wpsResult.pin)) {
+ wpsResult.status = WpsResult.Status.SUCCESS;
+ } else {
+ Log.e(TAG, "Failed to start WPS pin method configuration");
+ wpsResult.status = WpsResult.Status.FAILURE;
+ }
break;
default:
wpsResult = new WpsResult(Status.FAILURE);
loge("Invalid setup for WPS");
break;
}
- mWifiConfigManager.setAndEnableLastSelectedConfiguration
- (WifiConfiguration.INVALID_NETWORK_ID);
if (wpsResult.status == Status.SUCCESS) {
replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
transitionTo(mWpsRunningState);
@@ -6275,12 +5206,9 @@
// right ScanDetail to populate metrics.
String someBssid = (String) message.obj;
if (someBssid != null) {
- //Get the config associated with this connection attempt
- WifiConfiguration someConf =
- mWifiConfigManager.getWifiConfiguration(mTargetNetworkId);
- // Get the ScanDetail associated with this BSSID
- ScanDetailCache scanDetailCache = mWifiConfigManager.getScanDetailCache(
- someConf);
+ // Get the ScanDetail associated with this BSSID.
+ ScanDetailCache scanDetailCache =
+ mWifiConfigManager.getScanDetailCacheForNetwork(mTargetNetworkId);
if (scanDetailCache != null) {
mWifiMetrics.setConnectionScanDetail(scanDetailCache.getScanDetail(
someBssid));
@@ -6288,16 +5216,42 @@
}
return NOT_HANDLED;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
- if (DBG) log("Network connection established");
- mLastNetworkId = message.arg1;
+ if (mVerboseLoggingEnabled) log("Network connection established");
+ mLastNetworkId = lookupFrameworkNetworkId(message.arg1);
mLastBssid = (String) message.obj;
-
- mWifiInfo.setBSSID(mLastBssid);
- mWifiInfo.setNetworkId(mLastNetworkId);
- mWifiQualifiedNetworkSelector
- .enableBssidForQualityNetworkSelection(mLastBssid, true);
- sendNetworkStateChangeBroadcast(mLastBssid);
- transitionTo(mObtainingIpState);
+ reasonCode = message.arg2;
+ // TODO: This check should not be needed after WifiStateMachinePrime refactor.
+ // Currently, the last connected network configuration is left in
+ // wpa_supplicant, this may result in wpa_supplicant initiating connection
+ // to it after a config store reload. Hence the old network Id lookups may not
+ // work, so disconnect the network and let network selector reselect a new
+ // network.
+ config = getCurrentWifiConfiguration();
+ if (config != null) {
+ mWifiInfo.setBSSID(mLastBssid);
+ mWifiInfo.setNetworkId(mLastNetworkId);
+ mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode);
+ // We need to get the updated pseudonym from supplicant for EAP-SIM/AKA/AKA'
+ if (config.enterpriseConfig != null
+ && TelephonyUtil.isSimEapMethod(
+ config.enterpriseConfig.getEapMethod())) {
+ String anonymousIdentity = mWifiNative.getEapAnonymousIdentity();
+ if (anonymousIdentity != null) {
+ config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity);
+ } else {
+ Log.d(TAG, "Failed to get updated anonymous identity"
+ + " from supplicant, reset it in WifiConfiguration.");
+ config.enterpriseConfig.setAnonymousIdentity(null);
+ }
+ mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
+ }
+ sendNetworkStateChangeBroadcast(mLastBssid);
+ transitionTo(mObtainingIpState);
+ } else {
+ logw("Connected to unknown networkId " + mLastNetworkId
+ + ", disconnecting...");
+ sendMessage(CMD_DISCONNECT);
+ }
break;
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
// Calling handleNetworkDisconnect here is redundant because we might already
@@ -6309,39 +5263,48 @@
// 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;
- case CMD_ADD_PASSPOINT_MO:
- res = mWifiConfigManager.addPasspointManagementObject((String) message.obj);
- replyToMessage(message, message.what, res);
- break;
- case CMD_MODIFY_PASSPOINT_MO:
- if (message.obj != null) {
- Bundle bundle = (Bundle) message.obj;
- ArrayList<PasspointManagementObjectDefinition> mos =
- bundle.getParcelableArrayList("MOS");
- res = mWifiConfigManager.modifyPasspointMo(bundle.getString("FQDN"), mos);
- } else {
- res = 0;
- }
- replyToMessage(message, message.what, res);
-
- break;
case CMD_QUERY_OSU_ICON:
- if (mWifiConfigManager.queryPasspointIcon(
- ((Bundle) message.obj).getLong("BSSID"),
- ((Bundle) message.obj).getString("FILENAME"))) {
- res = 1;
- } else {
- res = 0;
- }
- replyToMessage(message, message.what, res);
+ mPasspointManager.queryPasspointIcon(
+ ((Bundle) message.obj).getLong(EXTRA_OSU_ICON_QUERY_BSSID),
+ ((Bundle) message.obj).getString(EXTRA_OSU_ICON_QUERY_FILENAME));
break;
case CMD_MATCH_PROVIDER_NETWORK:
- res = mWifiConfigManager.matchProviderWithCurrentNetwork((String) message.obj);
- replyToMessage(message, message.what, res);
+ // TODO(b/31065385): Passpoint config management.
+ replyToMessage(message, message.what, 0);
+ break;
+ case CMD_ADD_OR_UPDATE_PASSPOINT_CONFIG:
+ PasspointConfiguration passpointConfig = (PasspointConfiguration) message.obj;
+ if (mPasspointManager.addOrUpdateProvider(passpointConfig, message.arg1)) {
+ String fqdn = passpointConfig.getHomeSp().getFqdn();
+ if (isProviderOwnedNetwork(mTargetNetworkId, fqdn)
+ || isProviderOwnedNetwork(mLastNetworkId, fqdn)) {
+ logd("Disconnect from current network since its provider is updated");
+ sendMessage(CMD_DISCONNECT);
+ }
+ replyToMessage(message, message.what, SUCCESS);
+ } else {
+ replyToMessage(message, message.what, FAILURE);
+ }
+ break;
+ case CMD_REMOVE_PASSPOINT_CONFIG:
+ String fqdn = (String) message.obj;
+ if (mPasspointManager.removeProvider(fqdn)) {
+ if (isProviderOwnedNetwork(mTargetNetworkId, fqdn)
+ || isProviderOwnedNetwork(mLastNetworkId, fqdn)) {
+ logd("Disconnect from current network since its provider is removed");
+ sendMessage(CMD_DISCONNECT);
+ }
+ replyToMessage(message, message.what, SUCCESS);
+ } else {
+ replyToMessage(message, message.what, FAILURE);
+ }
+ break;
+ case CMD_ENABLE_P2P:
+ p2pSendMessage(WifiStateMachine.CMD_ENABLE_P2P);
break;
default:
return NOT_HANDLED;
@@ -6353,9 +5316,7 @@
private void updateCapabilities(WifiConfiguration config) {
NetworkCapabilities networkCapabilities = new NetworkCapabilities(mDfltNetworkCapabilities);
if (config != null) {
- Log.d(TAG, "updateCapabilities for config:" + config.getPrintableSsid() + config.ephemeral +
- "," + config.isCarrierNetwork);
- if (config.ephemeral && !config.isCarrierNetwork) {
+ if (config.ephemeral) {
networkCapabilities.removeCapability(
NetworkCapabilities.NET_CAPABILITY_TRUSTED);
} else {
@@ -6376,16 +5337,38 @@
mNetworkAgent.sendNetworkCapabilities(networkCapabilities);
}
+ /**
+ * Checks if the given network |networkdId| is provided by the given Passpoint provider with
+ * |providerFqdn|.
+ *
+ * @param networkId The ID of the network to check
+ * @param providerFqdn The FQDN of the Passpoint provider
+ * @return true if the given network is provided by the given Passpoint provider
+ */
+ private boolean isProviderOwnedNetwork(int networkId, String providerFqdn) {
+ if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
+ return false;
+ }
+ WifiConfiguration config = mWifiConfigManager.getConfiguredNetwork(networkId);
+ if (config == null) {
+ return false;
+ }
+ return TextUtils.equals(config.FQDN, providerFqdn);
+ }
+
private class WifiNetworkAgent extends NetworkAgent {
public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
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);
}
@@ -6393,14 +5376,17 @@
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));
}
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK);
doNetworkStatus(status);
}
}
@@ -6526,24 +5512,26 @@
// Full badn scans with exponential backoff for the purpose or extended roaming and
// network switching are performed unconditionally.
ScanDetailCache scanDetailCache =
- mWifiConfigManager.getScanDetailCache(config);
+ mWifiConfigManager.getScanDetailCacheForNetwork(config.networkId);
if (scanDetailCache == null
|| !config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)
|| scanDetailCache.size() > 6) {
//return true but to not trigger the scan
return true;
}
- HashSet<Integer> freqs = mWifiConfigManager.makeChannelList(config, ONE_HOUR_MILLI);
+ Set<Integer> freqs =
+ mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(
+ config.networkId, ONE_HOUR_MILLI, mWifiInfo.getFrequency());
if (freqs != null && freqs.size() != 0) {
- //if (DBG) {
+ //if (mVerboseLoggingEnabled) {
logd("starting scan for " + config.configKey() + " with " + freqs);
//}
- Set<Integer> hiddenNetworkIds = new HashSet<>();
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks = new ArrayList<>();
if (config.hiddenSSID) {
- hiddenNetworkIds.add(config.networkId);
+ hiddenNetworks.add(new WifiScanner.ScanSettings.HiddenNetwork(config.SSID));
}
// Call wifi native to start the scan
- if (startScanNative(freqs, hiddenNetworkIds, WIFI_WORK_SOURCE)) {
+ if (startScanNative(freqs, hiddenNetworks, WIFI_WORK_SOURCE)) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_OK;
} else {
// used for debug only, mark scan as failed
@@ -6551,33 +5539,11 @@
}
return true;
} else {
- if (DBG) logd("no channels for " + config.configKey());
+ if (mVerboseLoggingEnabled) logd("no channels for " + config.configKey());
return false;
}
}
- void clearCurrentConfigBSSID(String dbg) {
- // Clear the bssid in the current config's network block
- WifiConfiguration config = getCurrentWifiConfiguration();
- if (config == null)
- return;
- clearConfigBSSID(config, dbg);
- }
- void clearConfigBSSID(WifiConfiguration config, String dbg) {
- if (config == null)
- return;
- if (DBG) {
- logd(dbg + " " + mTargetRoamBSSID + " config " + config.configKey()
- + " config.NetworkSelectionStatus.mNetworkSelectionBSSID "
- + config.getNetworkSelectionStatus().getNetworkSelectionBSSID());
- }
- if (DBG) {
- logd(dbg + " " + config.SSID
- + " nid=" + Integer.toString(config.networkId));
- }
- mWifiConfigManager.saveWifiConfigBSSID(config, "any");
- }
-
class L2ConnectedState extends State {
@Override
public void enter() {
@@ -6598,7 +5564,7 @@
// We must clear the config BSSID, as the wifi chipset may decide to roam
// from this point on and having the BSSID specified in the network block would
// cause the roam to faile and the device to disconnect
- clearCurrentConfigBSSID("L2ConnectedState");
+ clearTargetBssid("L2ConnectedState");
mCountryCode.setReadyForChange(false);
mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED);
}
@@ -6612,7 +5578,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) {
@@ -6666,21 +5632,32 @@
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);
- handleIpReachabilityLost();
- transitionTo(mDisconnectingState);
+ if (mVerboseLoggingEnabled && message.obj != null) log((String) message.obj);
+ if (mIpReachabilityDisconnectEnabled) {
+ handleIpReachabilityLost();
+ transitionTo(mDisconnectingState);
+ } else {
+ logd("CMD_IP_REACHABILITY_LOST but disconnect disabled -- ignore");
+ }
break;
case CMD_DISCONNECT:
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
+ StaEvent.DISCONNECT_UNKNOWN);
mWifiNative.disconnect();
transitionTo(mDisconnectingState);
break;
case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST:
if (message.arg1 == 1) {
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
+ StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST);
mWifiNative.disconnect();
mTemporarilyDisconnectWifi = true;
transitionTo(mDisconnectingState);
@@ -6690,35 +5667,32 @@
if (message.arg1 != CONNECT_MODE) {
sendMessage(CMD_DISCONNECT);
deferMessage(message);
- if (message.arg1 == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
- noteWifiDisabledWhileAssociated();
- }
}
- mWifiConfigManager.
- setAndEnableLastSelectedConfiguration(
- WifiConfiguration.INVALID_NETWORK_ID);
break;
/* Ignore connection to same network */
case WifiManager.CONNECT_NETWORK:
int netId = message.arg1;
if (mWifiInfo.getNetworkId() == netId) {
+ replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
break;
}
return NOT_HANDLED;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
mWifiInfo.setBSSID((String) message.obj);
- mLastNetworkId = message.arg1;
+ mLastNetworkId = lookupFrameworkNetworkId(message.arg1);
mWifiInfo.setNetworkId(mLastNetworkId);
- if(!mLastBssid.equals((String) message.obj)) {
+ if(!mLastBssid.equals(message.obj)) {
mLastBssid = (String) message.obj;
sendNetworkStateChangeBroadcast(mLastBssid);
}
break;
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 (mEnableChipWakeUpWhenAssociated) {
+ 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
@@ -6729,25 +5703,21 @@
}
// Get Info and continue polling
fetchRssiLinkSpeedAndFrequencyNative();
- mWifiScoreReport =
- WifiScoreReport.calculateScore(mWifiInfo,
- getCurrentWifiConfiguration(),
- mWifiConfigManager,
- mNetworkAgent,
- mWifiScoreReport,
- mAggressiveHandover,
- mWifiMetrics);
+ // Send the update score to network agent.
+ mWifiScoreReport.calculateAndReportScore(
+ mWifiInfo, mNetworkAgent, mAggressiveHandover,
+ mWifiMetrics);
}
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
}
break;
case CMD_ENABLE_RSSI_POLL:
cleanWifiScore();
- if (mWifiConfigManager.mEnableRssiPollWhenAssociated.get()) {
+ if (mEnableRssiPollWhenAssociated) {
mEnableRssiPolling = (message.arg1 == 1);
} else {
mEnableRssiPolling = false;
@@ -6764,11 +5734,18 @@
RssiPacketCountInfo info = new RssiPacketCountInfo();
fetchRssiLinkSpeedAndFrequencyNative();
info.rssi = mWifiInfo.getRssi();
- fetchPktcntNative(info);
- replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
+ WifiNative.TxPacketCounters counters = mWifiNative.getTxPacketCounters();
+ if (counters != null) {
+ info.txgood = counters.txSucceeded;
+ info.txbad = counters.txFailed;
+ replyToMessage(message, WifiManager.RSSI_PKTCNT_FETCH_SUCCEEDED, info);
+ } else {
+ replyToMessage(message,
+ WifiManager.RSSI_PKTCNT_FETCH_FAILED, WifiManager.ERROR);
+ }
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 "
@@ -6778,7 +5755,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
@@ -6811,8 +5788,10 @@
if (message.arg1 == 0 // sim was removed
&& mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) {
WifiConfiguration config =
- mWifiConfigManager.getWifiConfiguration(mLastNetworkId);
+ mWifiConfigManager.getConfiguredNetwork(mLastNetworkId);
if (TelephonyUtil.isSimConfig(config)) {
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
+ StaEvent.DISCONNECT_RESET_SIM_NETWORKS);
mWifiNative.disconnect();
transitionTo(mDisconnectingState);
}
@@ -6830,21 +5809,23 @@
class ObtainingIpState extends State {
@Override
public void enter() {
- if (DBG) {
+ WifiConfiguration currentConfig = getCurrentWifiConfiguration();
+ boolean isUsingStaticIp =
+ (currentConfig.getIpAssignment() == IpConfiguration.IpAssignment.STATIC);
+ if (mVerboseLoggingEnabled) {
String key = "";
if (getCurrentWifiConfiguration() != null) {
key = getCurrentWifiConfiguration().configKey();
}
log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
+ " " + key + " "
- + " roam=" + mAutoRoaming
- + " static=" + mWifiConfigManager.isUsingStaticIp(mLastNetworkId)
- + " watchdog= " + obtainingIpWatchdogCount);
+ + " roam=" + mIsAutoRoaming
+ + " static=" + isUsingStaticIp);
}
// 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);
@@ -6852,7 +5833,7 @@
// We must clear the config BSSID, as the wifi chipset may decide to roam
// from this point on and having the BSSID specified in the network block would
// cause the roam to fail and the device to disconnect.
- clearCurrentConfigBSSID("ObtainingIpAddress");
+ clearTargetBssid("ObtainingIpAddress");
// Stop IpManager in case we're switching from DHCP to static
// configuration or vice versa.
@@ -6866,39 +5847,26 @@
// CONNECTED.
stopIpManager();
- mIpManager.setHttpProxy(mWifiConfigManager.getProxyProperties(mLastNetworkId));
+ mIpManager.setHttpProxy(currentConfig.getHttpProxy());
if (!TextUtils.isEmpty(mTcpBufferSizes)) {
mIpManager.setTcpBufferSizes(mTcpBufferSizes);
}
-
- if (!mWifiConfigManager.isUsingStaticIp(mLastNetworkId)) {
- final IpManager.ProvisioningConfiguration prov =
- mIpManager.buildProvisioningConfiguration()
+ final IpManager.ProvisioningConfiguration prov;
+ if (!isUsingStaticIp) {
+ prov = 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);
} else {
- StaticIpConfiguration config = mWifiConfigManager.getStaticIpConfiguration(
- mLastNetworkId);
- if (config.ipAddress == null) {
- logd("Static IP lacks address");
- sendMessage(CMD_IPV4_PROVISIONING_FAILURE);
- } else {
- final IpManager.ProvisioningConfiguration prov =
- mIpManager.buildProvisioningConfiguration()
- .withStaticConfiguration(config)
- .withApfCapabilities(mWifiNative.getApfCapabilities())
- .build();
- mIpManager.startProvisioning(prov);
- }
+ StaticIpConfiguration staticIpConfig = currentConfig.getStaticIpConfiguration();
+ prov = IpManager.buildProvisioningConfiguration()
+ .withStaticConfiguration(staticIpConfig)
+ .withApfCapabilities(mWifiNative.getApfCapabilities())
+ .build();
}
+ mIpManager.startProvisioning(prov);
+ // Get Link layer stats so as we get fresh tx packet counters
+ getWifiLinkLayerStats();
}
@Override
@@ -6906,17 +5874,14 @@
logStateAndMessage(message, this);
switch(message.what) {
- case CMD_AUTO_CONNECT:
- case CMD_AUTO_ROAM:
+ case CMD_START_CONNECT:
+ case CMD_START_ROAM:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
break;
case WifiManager.SAVE_NETWORK:
- case WifiStateMachine.CMD_AUTO_SAVE_NETWORK:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DEFERRED;
deferMessage(message);
break;
- /* Defer any power mode changes since we must keep active power mode at DHCP */
-
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION,
@@ -6931,16 +5896,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;
}
@@ -6952,17 +5907,19 @@
// If this network was explicitly selected by the user, evaluate whether to call
// explicitlySelected() so the system can treat it appropriately.
WifiConfiguration config = getCurrentWifiConfiguration();
- if (mWifiConfigManager.isLastSelectedConfiguration(config)) {
+ if (config == null) {
+ Log.wtf(TAG, "Current WifiConfiguration is null, but IP provisioning just succeeded");
+ } else if (mWifiConfigManager.getLastSelectedNetwork() == config.networkId) {
boolean prompt =
- mWifiConfigManager.checkConfigOverridePermission(config.lastConnectUid);
- if (DBG) {
+ mWifiPermissionsUtil.checkConfigOverridePermission(config.lastConnectUid);
+ 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);
@@ -6970,7 +5927,7 @@
}
setNetworkDetailedState(DetailedState.CONNECTED);
- mWifiConfigManager.updateStatus(mLastNetworkId, DetailedState.CONNECTED);
+ mWifiConfigManager.updateNetworkAfterConnect(mLastNetworkId);
sendNetworkStateChangeBroadcast(mLastBssid);
}
@@ -6978,7 +5935,7 @@
boolean mAssociated;
@Override
public void enter() {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("RoamingState Enter"
+ " mScreenOn=" + mScreenOn );
}
@@ -6998,13 +5955,14 @@
case CMD_IP_CONFIGURATION_LOST:
config = getCurrentWifiConfiguration();
if (config != null) {
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_AUTOROAM_FAILURE);
- mWifiConfigManager.noteRoamingFailure(config,
- WifiConfiguration.ROAMING_FAILURE_IP_CONFIG);
+ mWifiDiagnostics.captureBugReportData(
+ WifiDiagnostics.REPORT_REASON_AUTOROAM_FAILURE);
}
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) {
@@ -7023,7 +5981,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() );
}
@@ -7037,32 +5995,35 @@
// 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);
mRoamFailCount++;
handleNetworkDisconnect();
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
+ StaEvent.DISCONNECT_ROAM_WATCHDOG_TIMER);
mWifiNative.disconnect();
transitionTo(mDisconnectedState);
}
break;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
if (mAssociated) {
- if (DBG) log("roaming and Network connection established");
- mLastNetworkId = message.arg1;
+ if (mVerboseLoggingEnabled) {
+ log("roaming and Network connection established");
+ }
+ mLastNetworkId = lookupFrameworkNetworkId(message.arg1);
mLastBssid = (String) message.obj;
mWifiInfo.setBSSID(mLastBssid);
mWifiInfo.setNetworkId(mLastNetworkId);
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.trackBssid(mLastBssid, true);
- }
+ int reasonCode = message.arg2;
+ mWifiConnectivityManager.trackBssid(mLastBssid, true, reasonCode);
sendNetworkStateChangeBroadcast(mLastBssid);
// Successful framework roam! (probably)
@@ -7076,7 +6037,7 @@
// When transition from RoamingState to DisconnectingState or
// DisconnectedState, the config BSSID is cleared by
// handleNetworkDisconnect().
- clearCurrentConfigBSSID("RoamingCompleted");
+ clearTargetBssid("RoamingCompleted");
// We used to transition to ObtainingIpState in an
// attempt to do DHCPv4 RENEWs on framework roams.
@@ -7106,24 +6067,6 @@
transitionTo(mDisconnectedState);
}
break;
- case WifiMonitor.SSID_TEMP_DISABLED:
- // Auth error while roaming
- logd("SSID_TEMP_DISABLED nid=" + Integer.toString(mLastNetworkId)
- + " id=" + Integer.toString(message.arg1)
- + " isRoaming=" + isRoaming()
- + " roam=" + mAutoRoaming);
- if (message.arg1 == mLastNetworkId) {
- config = getCurrentWifiConfiguration();
- if (config != null) {
- mWifiLogger.captureBugReportData(
- WifiLogger.REPORT_REASON_AUTOROAM_FAILURE);
- mWifiConfigManager.noteRoamingFailure(config,
- WifiConfiguration.ROAMING_FAILURE_AUTH_FAILURE);
- }
- handleNetworkDisconnect();
- transitionTo(mDisconnectingState);
- }
- return NOT_HANDLED;
case CMD_START_SCAN:
deferMessage(message);
break;
@@ -7142,25 +6085,22 @@
class ConnectedState extends State {
@Override
public void enter() {
- String address;
updateDefaultRouteMacAddress(1000);
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
log("Enter ConnectedState "
+ " mScreenOn=" + mScreenOn);
}
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.handleConnectionStateChanged(
- WifiConnectivityManager.WIFI_STATE_CONNECTED);
- }
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_CONNECTED);
registerConnected();
lastConnectAttemptTimestamp = 0;
targetWificonfiguration = null;
// Paranoia
- linkDebouncing = false;
+ mIsLinkDebouncing = false;
// Not roaming anymore
- mAutoRoaming = false;
+ mIsAutoRoaming = false;
if (testNetworkDisconnect) {
testNetworkDisconnectCounter++;
@@ -7170,12 +6110,10 @@
testNetworkDisconnectCounter, 0), 15000);
}
- // Reenable all networks, allow for hidden networks to be scanned
- mWifiConfigManager.enableAllNetworks();
-
mLastDriverRoamAttempt = 0;
mTargetNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
- mWifiLastResortWatchdog.connectedStateTransition(true);
+ mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(true);
+ mWifiStateTracker.updateState(WifiStateTracker.CONNECTED);
}
@Override
public boolean processMessage(Message message) {
@@ -7183,11 +6121,10 @@
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) {
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
+ StaEvent.DISCONNECT_UNWANTED);
mWifiNative.disconnect();
transitionTo(mDisconnectingState);
} else if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN ||
@@ -7199,19 +6136,14 @@
if (config != null) {
// Disable autojoin
if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN) {
- config.validatedInternetAccess = false;
- // Clear last-selected status, as being last-selected also avoids
- // disabling auto-join.
- if (mWifiConfigManager.isLastSelectedConfiguration(config)) {
- mWifiConfigManager.setAndEnableLastSelectedConfiguration(
- WifiConfiguration.INVALID_NETWORK_ID);
- }
- mWifiConfigManager.updateNetworkSelectionStatus(config,
+ mWifiConfigManager.setNetworkValidatedInternetAccess(
+ config.networkId, false);
+ mWifiConfigManager.updateNetworkSelectionStatus(config.networkId,
WifiConfiguration.NetworkSelectionStatus
.DISABLED_NO_INTERNET);
}
- config.numNoInternetAccessReports += 1;
- mWifiConfigManager.writeKnownNetworkHistory();
+ mWifiConfigManager.incrementNetworkNoInternetAccessReports(
+ config.networkId);
}
}
return HANDLED;
@@ -7220,19 +6152,14 @@
config = getCurrentWifiConfiguration();
if (config != null) {
// re-enable autojoin
- config.numNoInternetAccessReports = 0;
- config.validatedInternetAccess = true;
- mWifiConfigManager.writeKnownNetworkHistory();
+ mWifiConfigManager.setNetworkValidatedInternetAccess(
+ config.networkId, true);
}
}
return HANDLED;
case CMD_ACCEPT_UNVALIDATED:
boolean accept = (message.arg1 != 0);
- config = getCurrentWifiConfiguration();
- if (config != null) {
- config.noInternetAccessExpected = accept;
- mWifiConfigManager.writeKnownNetworkHistory();
- }
+ mWifiConfigManager.setNetworkNoInternetAccessExpected(mLastNetworkId, accept);
return HANDLED;
case CMD_TEST_NETWORK_DISCONNECT:
// Force a disconnect
@@ -7243,7 +6170,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;
@@ -7252,27 +6179,28 @@
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)) {
- mWifiLogger.captureBugReportData(
- WifiLogger.REPORT_REASON_UNEXPECTED_DISCONNECT);
+ mWifiDiagnostics.captureBugReportData(
+ WifiDiagnostics.REPORT_REASON_UNEXPECTED_DISCONNECT);
}
config = getCurrentWifiConfiguration();
- if (mScreenOn
- && !linkDebouncing
+ if (mEnableLinkDebouncing
+ && mScreenOn
+ && !isLinkDebouncing()
&& config != null
&& config.getNetworkSelectionStatus().isNetworkEnabled()
- && !mWifiConfigManager.isLastSelectedConfiguration(config)
+ && config.networkId != mWifiConfigManager.getLastSelectedNetwork()
&& (message.arg2 != 3 /* reason cannot be 3, i.e. locally generated */
|| (lastRoam > 0 && lastRoam < 2000) /* unless driver is roaming */)
&& ((ScanResult.is24GHz(mWifiInfo.getFrequency())
&& mWifiInfo.getRssi() >
- WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND)
+ mThresholdQualifiedRssi5)
|| (ScanResult.is5GHz(mWifiInfo.getFrequency())
&& mWifiInfo.getRssi() >
- mWifiConfigManager.mThresholdQualifiedRssi5.get()))) {
+ mThresholdQualifiedRssi5))) {
// Start de-bouncing the L2 disconnection:
// this L2 disconnection might be spurious.
// Hence we allow 4 seconds for the state machine to try
@@ -7280,11 +6208,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()
@@ -7294,137 +6222,85 @@
}
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()));
}
}
break;
- case CMD_AUTO_ROAM:
+ case CMD_START_ROAM:
// Clear the driver roam indication since we are attempting a framework roam
mLastDriverRoamAttempt = 0;
- /*<TODO> 2016-02-24
- Fix CMD_AUTO_ROAM to use the candidate (message.arg1) networkID, rather than
- the old networkID.
- The current code only handles roaming between BSSIDs on the same networkID,
- and will break for roams between different (but linked) networkIDs. This
- case occurs for DBDC roaming, and the CMD_AUTO_ROAM sent due to it will
- fail.
- */
/* Connect command coming from auto-join */
+ int netId = message.arg1;
ScanResult candidate = (ScanResult)message.obj;
- String bssid = "any";
+ String bssid = SUPPLICANT_BSSID_ANY;
if (candidate != null) {
bssid = candidate.BSSID;
}
- int netId = message.arg1;
- if (netId == WifiConfiguration.INVALID_NETWORK_ID) {
- loge("AUTO_ROAM and no config, bail out...");
+ config = mWifiConfigManager.getConfiguredNetworkWithPassword(netId);
+ if (config == null) {
+ loge("CMD_START_ROAM and no config, bail out...");
break;
- } else {
- config = mWifiConfigManager.getWifiConfiguration(netId);
- if (config == null) {
- loge("AUTO_ROAM and invalid netowrk ID, bail out...");
- break;
- }
}
setTargetBssid(config, bssid);
mTargetNetworkId = netId;
- logd("CMD_AUTO_ROAM sup state "
+ logd("CMD_START_ROAM sup state "
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName()
+ " nid=" + Integer.toString(netId)
+ " config " + config.configKey()
+ " targetRoamBSSID " + mTargetRoamBSSID);
- /* Determine if this is a regular roam (between BSSIDs sharing the same SSID),
- or a DBDC roam (between 2.4 & 5GHz networks on different SSID's, but with
- matching 16 byte BSSID prefixes):
- */
- WifiConfiguration currentConfig = getCurrentWifiConfiguration();
- if (currentConfig != null && currentConfig.isLinked(config)) {
- // This is dual band roaming
- mWifiMetrics.startConnectionEvent(config, mTargetRoamBSSID,
- WifiMetricsProto.ConnectionEvent.ROAM_DBDC);
- } else {
- // This is regular roaming
- mWifiMetrics.startConnectionEvent(config, mTargetRoamBSSID,
- WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
- }
-
- if (deferForUserInput(message, netId, false)) {
- reportConnectionAttemptEnd(
- WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
- WifiMetricsProto.ConnectionEvent.HLF_NONE);
- break;
- } else if (mWifiConfigManager.getWifiConfiguration(netId).userApproved ==
- WifiConfiguration.USER_BANNED) {
- replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
- WifiManager.NOT_AUTHORIZED);
- reportConnectionAttemptEnd(
- WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
- WifiMetricsProto.ConnectionEvent.HLF_NONE);
- break;
- }
-
- boolean ret = false;
- if (mLastNetworkId != netId) {
- if (mWifiConfigManager.selectNetwork(config, /* updatePriorities = */ false,
- WifiConfiguration.UNKNOWN_UID) && mWifiNative.reconnect()) {
- ret = true;
- }
- } else {
- ret = mWifiNative.reassociate();
- }
- if (ret) {
- lastConnectAttemptTimestamp = System.currentTimeMillis();
- targetWificonfiguration = mWifiConfigManager.getWifiConfiguration(netId);
-
- // replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
- mAutoRoaming = true;
+ reportConnectionAttemptStart(config, mTargetRoamBSSID,
+ WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
+ if (mWifiNative.roamToNetwork(config)) {
+ lastConnectAttemptTimestamp = mClock.getWallClockMillis();
+ targetWificonfiguration = config;
+ mIsAutoRoaming = true;
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_ROAM, config);
transitionTo(mRoamingState);
-
} else {
- loge("Failed to connect config: " + config + " netId: " + netId);
+ loge("CMD_START_ROAM Failed to start roaming to network " + config);
+ reportConnectionAttemptEnd(
+ WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
+ WifiMetricsProto.ConnectionEvent.HLF_NONE);
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.ERROR);
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
- reportConnectionAttemptEnd(
- WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
- WifiMetricsProto.ConnectionEvent.HLF_NONE);
break;
}
break;
case CMD_START_IP_PACKET_OFFLOAD: {
- int slot = message.arg1;
- int intervalSeconds = message.arg2;
- KeepalivePacketData pkt = (KeepalivePacketData) message.obj;
- byte[] dstMac;
- try {
- InetAddress gateway = RouteInfo.selectBestRoute(
- mLinkProperties.getRoutes(), pkt.dstAddress).getGateway();
- String dstMacStr = macAddressFromRoute(gateway.getHostAddress());
- dstMac = macAddressFromString(dstMacStr);
- } catch (NullPointerException|IllegalArgumentException e) {
- loge("Can't find MAC address for next hop to " + pkt.dstAddress);
- mNetworkAgent.onPacketKeepaliveEvent(slot,
- ConnectivityManager.PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
- break;
- }
- pkt.dstMac = dstMac;
- int result = startWifiIPPacketOffload(slot, pkt, intervalSeconds);
- mNetworkAgent.onPacketKeepaliveEvent(slot, result);
+ int slot = message.arg1;
+ int intervalSeconds = message.arg2;
+ KeepalivePacketData pkt = (KeepalivePacketData) message.obj;
+ byte[] dstMac;
+ try {
+ InetAddress gateway = RouteInfo.selectBestRoute(
+ mLinkProperties.getRoutes(), pkt.dstAddress).getGateway();
+ String dstMacStr = macAddressFromRoute(gateway.getHostAddress());
+ dstMac = NativeUtil.macAddressToByteArray(dstMacStr);
+ } catch (NullPointerException | IllegalArgumentException e) {
+ loge("Can't find MAC address for next hop to " + pkt.dstAddress);
+ mNetworkAgent.onPacketKeepaliveEvent(slot,
+ ConnectivityManager.PacketKeepalive.ERROR_INVALID_IP_ADDRESS);
break;
}
+ pkt.dstMac = dstMac;
+ int result = startWifiIPPacketOffload(slot, pkt, intervalSeconds);
+ mNetworkAgent.onPacketKeepaliveEvent(slot, result);
+ break;
+ }
default:
return NOT_HANDLED;
}
@@ -7434,14 +6310,11 @@
@Override
public void exit() {
logd("WifiStateMachine: Leaving Connected state");
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.handleConnectionStateChanged(
- WifiConnectivityManager.WIFI_STATE_TRANSITIONING);
- }
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_TRANSITIONING);
mLastDriverRoamAttempt = 0;
- mWhiteListedSsids = null;
- mWifiLastResortWatchdog.connectedStateTransition(false);
+ mWifiInjector.getWifiLastResortWatchdog().connectedStateTransition(false);
}
}
@@ -7450,7 +6323,7 @@
@Override
public void enter() {
- if (DBG) {
+ if (mVerboseLoggingEnabled) {
logd(" Enter DisconnectingState State screenOn=" + mScreenOn);
}
@@ -7479,11 +6352,11 @@
deferMessage(message);
return HANDLED;
case CMD_DISCONNECT:
- if (DBG) log("Ignore CMD_DISCONNECT when already disconnecting.");
+ if (mVerboseLoggingEnabled) log("Ignore CMD_DISCONNECT when already disconnecting.");
break;
case CMD_DISCONNECTING_WATCHDOG_TIMER:
if (disconnectingWatchdogCount == message.arg1) {
- if (DBG) log("disconnecting watchdog! -> disconnect");
+ if (mVerboseLoggingEnabled) log("disconnecting watchdog! -> disconnect");
handleNetworkDisconnect();
transitionTo(mDisconnectedState);
}
@@ -7508,24 +6381,23 @@
class DisconnectedState extends State {
@Override
public void enter() {
+ Log.i(TAG, "disconnectedstate enter");
// 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);
}
/** clear the roaming state, if we were roaming, we failed */
- mAutoRoaming = false;
+ mIsAutoRoaming = false;
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.handleConnectionStateChanged(
- WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
- }
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
/**
* If we have no networks saved, the supplicant stops doing the periodic scan.
@@ -7538,7 +6410,8 @@
++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
}
- mDisconnectedTimeStamp = System.currentTimeMillis();
+ mDisconnectedTimeStamp = mClock.getWallClockMillis();
+ mWifiStateTracker.updateState(WifiStateTracker.DISCONNECTED);
}
@Override
public boolean processMessage(Message message) {
@@ -7570,28 +6443,20 @@
case CMD_SET_OPERATIONAL_MODE:
if (message.arg1 != CONNECT_MODE) {
mOperationalMode = message.arg1;
- mWifiConfigManager.disableAllNetworksNative();
- if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
- mWifiP2pChannel.sendMessage(CMD_DISABLE_P2P_REQ);
+ if (mOperationalMode == DISABLED_MODE) {
+ transitionTo(mSupplicantStoppingState);
+ } else if (mOperationalMode == SCAN_ONLY_MODE
+ || mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {
+ p2pSendMessage(CMD_DISABLE_P2P_REQ);
setWifiState(WIFI_STATE_DISABLED);
+ transitionTo(mScanModeState);
}
- transitionTo(mScanModeState);
}
- mWifiConfigManager.
- setAndEnableLastSelectedConfiguration(
- WifiConfiguration.INVALID_NETWORK_ID);
break;
case CMD_DISCONNECT:
- if (SupplicantState.isConnecting(mWifiInfo.getSupplicantState())) {
- if (DBG) {
- log("CMD_DISCONNECT when supplicant is connecting - do not ignore");
- }
- mWifiConfigManager.setAndEnableLastSelectedConfiguration(
- WifiConfiguration.INVALID_NETWORK_ID);
- mWifiNative.disconnect();
- break;
- }
- if (DBG) log("Ignore CMD_DISCONNECT when already disconnected.");
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_FRAMEWORK_DISCONNECT,
+ StaEvent.DISCONNECT_UNKNOWN);
+ mWifiNative.disconnect();
break;
/* Ignore network disconnect */
case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
@@ -7599,10 +6464,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 */
@@ -7620,15 +6485,8 @@
case WifiP2pServiceImpl.P2P_CONNECTION_CHANGED:
NetworkInfo info = (NetworkInfo) message.obj;
mP2pConnected.set(info.isConnected());
- if (mP2pConnected.get()) {
- int defaultInterval = mContext.getResources().getInteger(
- R.integer.config_wifi_scan_interval_p2p_connected);
- long scanIntervalMs = mFacade.getLongSetting(mContext,
- Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
- defaultInterval);
- mWifiNative.setScanInterval((int) scanIntervalMs/1000);
- } else if (mWifiConfigManager.getSavedNetworks().size() == 0) {
- if (DBG) log("Turn on scanning after p2p disconnected");
+ if (!mP2pConnected.get() && mWifiConfigManager.getSavedNetworks().size() == 0) {
+ if (mVerboseLoggingEnabled) log("Turn on scanning after p2p disconnected");
sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);
}
@@ -7655,13 +6513,28 @@
@Override
public void exit() {
- if (mWifiConnectivityManager != null) {
- mWifiConnectivityManager.handleConnectionStateChanged(
- WifiConnectivityManager.WIFI_STATE_TRANSITIONING);
- }
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_TRANSITIONING);
}
}
+ /**
+ * WPS connection flow:
+ * 1. WifiStateMachine receive WPS_START message from WifiManager API.
+ * 2. WifiStateMachine initiates the appropriate WPS operation using WifiNative methods:
+ * {@link WifiNative#startWpsPbc(String)}, {@link WifiNative#startWpsPinDisplay(String)}, etc.
+ * 3. WifiStateMachine then transitions to this WpsRunningState.
+ * 4a. Once WifiStateMachine receive the connected event:
+ * {@link WifiMonitor#NETWORK_CONNECTION_EVENT},
+ * 4a.1 Load the network params out of wpa_supplicant.
+ * 4a.2 Add the network with params to WifiConfigManager.
+ * 4a.3 Enable the network with |disableOthers| set to true.
+ * 4a.4 Send a response to the original source of WifiManager API using {@link #mSourceMessage}.
+ * 4b. Any failures are notified to the original source of WifiManager API
+ * using {@link #mSourceMessage}.
+ * 5. We then transition to disconnected state and let network selection reconnect to the newly
+ * added network.
+ */
class WpsRunningState extends State {
// Tracks the source to provide a reply
private Message mSourceMessage;
@@ -7678,7 +6551,12 @@
// Ignore intermediate success, wait for full connection
break;
case WifiMonitor.NETWORK_CONNECTION_EVENT:
- replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
+ if (loadNetworksFromSupplicantAfterWps()) {
+ replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
+ } else {
+ replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
+ WifiManager.ERROR);
+ }
mSourceMessage.recycle();
mSourceMessage = null;
deferMessage(message);
@@ -7699,7 +6577,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:
@@ -7724,35 +6604,35 @@
* Defer all commands that can cause connections to a different network
* or put the state machine out of connect mode
*/
- case CMD_STOP_DRIVER:
case CMD_SET_OPERATIONAL_MODE:
case WifiManager.CONNECT_NETWORK:
case CMD_ENABLE_NETWORK:
case CMD_RECONNECT:
case CMD_REASSOCIATE:
- case CMD_ENABLE_ALL_NETWORKS:
deferMessage(message);
break;
- case CMD_AUTO_CONNECT:
- case CMD_AUTO_ROAM:
+ case CMD_START_CONNECT:
+ case CMD_START_ROAM:
messageHandlingStatus = MESSAGE_HANDLING_STATUS_DISCARD;
return HANDLED;
case CMD_START_SCAN:
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.
@@ -7765,15 +6645,41 @@
return HANDLED;
}
- @Override
- public void exit() {
- mWifiConfigManager.enableAllNetworks();
- mWifiConfigManager.loadConfiguredNetworks();
+ /**
+ * Load network config from wpa_supplicant after WPS is complete.
+ */
+ private boolean loadNetworksFromSupplicantAfterWps() {
+ Map<String, WifiConfiguration> configs = new HashMap<>();
+ SparseArray<Map<String, String>> extras = new SparseArray<>();
+ if (!mWifiNative.migrateNetworksFromSupplicant(configs, extras)) {
+ loge("Failed to load networks from wpa_supplicant after Wps");
+ return false;
+ }
+ for (Map.Entry<String, WifiConfiguration> entry : configs.entrySet()) {
+ WifiConfiguration config = entry.getValue();
+ // Reset the network ID retrieved from wpa_supplicant, since we want to treat
+ // this as a new network addition in framework.
+ config.networkId = WifiConfiguration.INVALID_NETWORK_ID;
+ NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork(
+ config, mSourceMessage.sendingUid);
+ if (!result.isSuccess()) {
+ loge("Failed to add network after WPS: " + entry.getValue());
+ return false;
+ }
+ if (!mWifiConfigManager.enableNetwork(
+ result.getNetworkId(), true, mSourceMessage.sendingUid)) {
+ loge("Failed to enable network after WPS: " + entry.getValue());
+ return false;
+ }
+ }
+ return true;
}
}
class SoftApState extends State {
private SoftApManager mSoftApManager;
+ private String mIfaceName;
+ private int mMode;
private class SoftApListener implements SoftApManager.Listener {
@Override
@@ -7784,42 +6690,53 @@
sendMessage(CMD_START_AP_FAILURE);
}
- setWifiApState(state, reason);
+ setWifiApState(state, reason, mIfaceName, mMode);
}
}
@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.getCountryCode(),
- mWifiApConfigStore.getAllowed2GChannel(),
- new SoftApListener());
- mSoftApManager.start(config);
- } else {
+ if (message.what != CMD_START_AP) {
throw new RuntimeException("Illegal transition to SoftApState: " + message);
}
+ SoftApModeConfiguration config = (SoftApModeConfiguration) message.obj;
+ mMode = config.getTargetMode();
+
+ IApInterface apInterface = mWifiNative.setupForSoftApMode();
+ if (apInterface == null) {
+ setWifiApState(WIFI_AP_STATE_FAILED,
+ WifiManager.SAP_START_FAILURE_GENERAL, null, mMode);
+ /**
+ * Transition to InitialState to reset the
+ * driver/HAL back to the initial state.
+ */
+ transitionTo(mInitialState);
+ return;
+ }
+
+ try {
+ mIfaceName = apInterface.getInterfaceName();
+ } catch (RemoteException e) {
+ // Failed to get the interface name. The name will not be available for
+ // the enabled broadcast, but since we had an error getting the name, we most likely
+ // won't be able to fully start softap mode.
+ }
+
+ checkAndSetConnectivityInstance();
+ mSoftApManager = mWifiInjector.makeSoftApManager(mNwService,
+ new SoftApListener(),
+ apInterface,
+ config.getWifiConfiguration());
+ mSoftApManager.start();
+ mWifiStateTracker.updateState(WifiStateTracker.SOFT_AP);
}
@Override
public void exit() {
mSoftApManager = null;
+ mIfaceName = null;
+ mMode = WifiManager.IFACE_IP_MODE_UNSPECIFIED;
}
@Override
@@ -7883,8 +6800,12 @@
}
/**
+ * Notify interested parties if a wifi config has been changed.
+ *
* @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
+ * TODO: b/35258354 investigate if this can be removed. Is the broadcast sent by
+ * WifiConfigManager sufficient?
*/
private void broadcastWifiCredentialChanged(int wifiCredentialEventType,
WifiConfiguration config) {
@@ -7898,294 +6819,41 @@
}
}
- private static int parseHex(char ch) {
- if ('0' <= ch && ch <= '9') {
- return ch - '0';
- } else if ('a' <= ch && ch <= 'f') {
- return ch - 'a' + 10;
- } else if ('A' <= ch && ch <= 'F') {
- return ch - 'A' + 10;
- } else {
- throw new NumberFormatException("" + ch + " is not a valid hex digit");
- }
- }
-
- private byte[] parseHex(String hex) {
- /* This only works for good input; don't throw bad data at it */
- if (hex == null) {
- return new byte[0];
- }
-
- if (hex.length() % 2 != 0) {
- throw new NumberFormatException(hex + " is not a valid hex string");
- }
-
- byte[] result = new byte[(hex.length())/2 + 1];
- result[0] = (byte) ((hex.length())/2);
- for (int i = 0, j = 1; i < hex.length(); i += 2, j++) {
- int val = parseHex(hex.charAt(i)) * 16 + parseHex(hex.charAt(i+1));
- byte b = (byte) (val & 0xFF);
- result[j] = b;
- }
-
- return result;
- }
-
- private static String makeHex(byte[] bytes) {
- StringBuilder sb = new StringBuilder();
- for (byte b : bytes) {
- sb.append(String.format("%02x", b));
- }
- return sb.toString();
- }
-
- private static String makeHex(byte[] bytes, int from, int len) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < len; i++) {
- sb.append(String.format("%02x", bytes[from+i]));
- }
- 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;
-
- byte[] result = new byte[len];
-
- int index = 0;
- if (array1.length != 0) {
- for (byte b : array1) {
- result[index] = b;
- index++;
- }
- }
-
- if (array2.length != 0) {
- for (byte b : array2) {
- result[index] = b;
- index++;
- }
- }
-
- return result;
- }
-
- // TODO move to TelephonyUtil, same with utilities above
- String getGsmSimAuthResponse(String[] requestData, TelephonyManager tm) {
- StringBuilder sb = new StringBuilder();
- for (String challenge : requestData) {
- if (challenge == null || challenge.isEmpty()) {
- continue;
- }
- logd("RAND = " + challenge);
-
- byte[] rand = null;
- try {
- rand = parseHex(challenge);
- } catch (NumberFormatException e) {
- loge("malformed challenge");
- continue;
- }
-
- String base64Challenge = android.util.Base64.encodeToString(
- rand, android.util.Base64.NO_WRAP);
-
- // Try USIM first for authentication.
- String tmResponse = tm.getIccAuthentication(tm.APPTYPE_USIM,
- tm.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);
- }
- logv("Raw Response - " + tmResponse);
-
- if (tmResponse == null || tmResponse.length() <= 4) {
- loge("bad response - " + tmResponse);
- return null;
- }
-
- byte[] result = android.util.Base64.decode(tmResponse, android.util.Base64.DEFAULT);
- logv("Hex Response -" + makeHex(result));
- int sres_len = result[0];
- if (sres_len >= result.length) {
- loge("malfomed response - " + tmResponse);
- return null;
- }
- String sres = makeHex(result, 1, sres_len);
- int kc_offset = 1 + sres_len;
- if (kc_offset >= result.length) {
- loge("malfomed response - " + tmResponse);
- return null;
- }
- int kc_len = result[kc_offset];
- if (kc_offset + kc_len > result.length) {
- loge("malfomed response - " + tmResponse);
- return null;
- }
- String kc = makeHex(result, 1 + kc_offset, kc_len);
- sb.append(":" + kc + ":" + sres);
- logv("kc:" + kc + " sres:" + sres);
- }
-
- return sb.toString();
- }
-
- // TODO move to TelephonyUtil
void handleGsmAuthRequest(SimAuthRequestData requestData) {
if (targetWificonfiguration == null
- || targetWificonfiguration.networkId == requestData.networkId) {
+ || targetWificonfiguration.networkId
+ == lookupFrameworkNetworkId(requestData.networkId)) {
logd("id matches targetWifiConfiguration");
} else {
logd("id does not match targetWifiConfiguration");
return;
}
- TelephonyManager tm = (TelephonyManager)
- mContext.getSystemService(Context.TELEPHONY_SERVICE);
-
- if (tm == null) {
- loge("could not get telephony manager");
- mWifiNative.simAuthFailedResponse(requestData.networkId);
- return;
- }
-
- String response = getGsmSimAuthResponse(requestData.data, tm);
+ String response =
+ TelephonyUtil.getGsmSimAuthResponse(requestData.data, getTelephonyManager());
if (response == null) {
mWifiNative.simAuthFailedResponse(requestData.networkId);
} else {
logv("Supplicant Response -" + response);
- mWifiNative.simAuthResponse(requestData.networkId, "GSM-AUTH", response);
+ mWifiNative.simAuthResponse(requestData.networkId,
+ WifiNative.SIM_AUTH_RESP_TYPE_GSM_AUTH, response);
}
}
- // TODO move to TelephonyUtil
void handle3GAuthRequest(SimAuthRequestData requestData) {
- StringBuilder sb = new StringBuilder();
- byte[] rand = null;
- byte[] authn = null;
- String res_type = "UMTS-AUTH";
-
if (targetWificonfiguration == null
- || targetWificonfiguration.networkId == requestData.networkId) {
+ || targetWificonfiguration.networkId
+ == lookupFrameworkNetworkId(requestData.networkId)) {
logd("id matches targetWifiConfiguration");
} else {
logd("id does not match targetWifiConfiguration");
return;
}
- if (requestData.data.length == 2) {
- try {
- rand = parseHex(requestData.data[0]);
- authn = parseHex(requestData.data[1]);
- } catch (NumberFormatException e) {
- loge("malformed challenge");
- }
- } else {
- loge("malformed challenge");
- }
- String tmResponse = "";
- if (rand != null && authn != null) {
- String base64Challenge = android.util.Base64.encodeToString(
- concatHex(rand,authn), android.util.Base64.NO_WRAP);
-
- TelephonyManager tm = (TelephonyManager)
- mContext.getSystemService(Context.TELEPHONY_SERVICE);
- if (tm != null) {
- tmResponse = tm.getIccAuthentication(tm.APPTYPE_USIM,
- tm.AUTHTYPE_EAP_AKA, base64Challenge);
- logv("Raw Response - " + tmResponse);
- } else {
- loge("could not get telephony manager");
- }
- }
-
- boolean good_response = false;
- if (tmResponse != null && tmResponse.length() > 4) {
- byte[] result = android.util.Base64.decode(tmResponse,
- android.util.Base64.DEFAULT);
- loge("Hex Response - " + makeHex(result));
- byte tag = result[0];
- if (tag == (byte) 0xdb) {
- logv("successful 3G authentication ");
- int res_len = result[1];
- String res = makeHex(result, 2, res_len);
- int ck_len = result[res_len + 2];
- String ck = makeHex(result, res_len + 3, ck_len);
- int ik_len = result[res_len + ck_len + 3];
- String ik = makeHex(result, res_len + ck_len + 4, ik_len);
- sb.append(":" + ik + ":" + ck + ":" + res);
- logv("ik:" + ik + "ck:" + ck + " res:" + res);
- good_response = true;
- } else if (tag == (byte) 0xdc) {
- loge("synchronisation failure");
- int auts_len = result[1];
- String auts = makeHex(result, 2, auts_len);
- res_type = "UMTS-AUTS";
- sb.append(":" + auts);
- logv("auts:" + auts);
- good_response = true;
- } else {
- loge("bad response - unknown tag = " + tag);
- }
- } else {
- loge("bad response - " + tmResponse);
- }
-
- if (good_response) {
- String response = sb.toString();
- logv("Supplicant Response -" + response);
- mWifiNative.simAuthResponse(requestData.networkId, res_type, response);
+ SimAuthResponseData response =
+ TelephonyUtil.get3GAuthResponse(requestData, getTelephonyManager());
+ if (response != null) {
+ mWifiNative.simAuthResponse(requestData.networkId, response.type, response.response);
} else {
mWifiNative.umtsAuthFailedResponse(requestData.networkId);
}
@@ -8197,10 +6865,10 @@
* @param networkId ID of the network to connect to
* @param bssid BSSID of the network
*/
- public void autoConnectToNetwork(int networkId, String bssid) {
+ public void startConnectToNetwork(int networkId, String bssid) {
synchronized (mWifiReqCountLock) {
if (hasConnectionRequests()) {
- sendMessage(CMD_AUTO_CONNECT, networkId, 0, bssid);
+ sendMessage(CMD_START_CONNECT, networkId, 0, bssid);
}
}
}
@@ -8211,8 +6879,8 @@
* @param networkId ID of the network to roam to
* @param scanResult scan result which identifies the network to roam to
*/
- public void autoRoamToNetwork(int networkId, ScanResult scanResult) {
- sendMessage(CMD_AUTO_ROAM, networkId, 0, scanResult);
+ public void startRoamToNetwork(int networkId, ScanResult scanResult) {
+ sendMessage(CMD_START_ROAM, networkId, 0, scanResult);
}
/**
@@ -8246,47 +6914,48 @@
/**
* Update WifiMetrics before dumping
*/
- void updateWifiMetrics() {
- int numSavedNetworks = mWifiConfigManager.getConfiguredNetworksSize();
- int numOpenNetworks = 0;
- int numPersonalNetworks = 0;
- int numEnterpriseNetworks = 0;
- int numNetworksAddedByUser = 0;
- int numNetworksAddedByApps = 0;
- int numHiddenNetworks = 0;
- int numPasspoint = 0;
- for (WifiConfiguration config : mWifiConfigManager.getSavedNetworks()) {
- if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
- numOpenNetworks++;
- } else if (config.isEnterprise()) {
- numEnterpriseNetworks++;
- } else {
- numPersonalNetworks++;
- }
- if (config.selfAdded) {
- numNetworksAddedByUser++;
- } else {
- numNetworksAddedByApps++;
- }
- if (config.hiddenSSID) {
- numHiddenNetworks++;
- }
- if (config.isPasspoint()) {
- numPasspoint++;
- }
+ public void updateWifiMetrics() {
+ mWifiMetrics.updateSavedNetworks(mWifiConfigManager.getSavedNetworks());
+ }
+
+ /**
+ * Private method to handle calling WifiConfigManager to forget/remove network configs and reply
+ * to the message from the sender of the outcome.
+ *
+ * The current implementation requires that forget and remove be handled in different ways
+ * (responses are handled differently). In the interests of organization, the handling is all
+ * now in this helper method. TODO: b/35257965 is filed to track the possibility of merging
+ * the two call paths.
+ */
+ private boolean deleteNetworkConfigAndSendReply(Message message, boolean calledFromForget) {
+ boolean success = mWifiConfigManager.removeNetwork(message.arg1, message.sendingUid);
+ if (!success) {
+ loge("Failed to remove network");
}
- mWifiMetrics.setNumSavedNetworks(numSavedNetworks);
- mWifiMetrics.setNumOpenNetworks(numOpenNetworks);
- mWifiMetrics.setNumPersonalNetworks(numPersonalNetworks);
- mWifiMetrics.setNumEnterpriseNetworks(numEnterpriseNetworks);
- mWifiMetrics.setNumNetworksAddedByUser(numNetworksAddedByUser);
- mWifiMetrics.setNumNetworksAddedByApps(numNetworksAddedByApps);
- mWifiMetrics.setNumHiddenNetworks(numHiddenNetworks);
- mWifiMetrics.setNumPasspointNetworks(numPasspoint);
+
+ if (calledFromForget) {
+ if (success) {
+ replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
+ broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_FORGOT,
+ (WifiConfiguration) message.obj);
+ return true;
+ }
+ replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED, WifiManager.ERROR);
+ return false;
+ } else {
+ // Remaining calls are from the removeNetwork path
+ if (success) {
+ replyToMessage(message, message.what, SUCCESS);
+ return true;
+ }
+ messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
+ replyToMessage(message, message.what, FAILURE);
+ return false;
+ }
}
private static String getLinkPropertiesSummary(LinkProperties lp) {
- List<String> attributes = new ArrayList(6);
+ List<String> attributes = new ArrayList<>(6);
if (lp.hasIPv4Address()) {
attributes.add("v4");
}
@@ -8309,36 +6978,12 @@
return TextUtils.join(" ", attributes);
}
- private void wnmFrameReceived(WnmData event) {
- // %012x HS20-SUBSCRIPTION-REMEDIATION "%u %s", osu_method, url
- // %012x HS20-DEAUTH-IMMINENT-NOTICE "%u %u %s", code, reauth_delay, url
-
- Intent intent = new Intent(WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_BSSID, event.getBssid());
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_URL, event.getUrl());
-
- if (event.isDeauthEvent()) {
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_ESS, event.isEss());
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_DELAY, event.getDelay());
- } else {
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_METHOD, event.getMethod());
- WifiConfiguration config = getCurrentWifiConfiguration();
- if (config != null && config.FQDN != null) {
- intent.putExtra(WifiManager.EXTRA_PASSPOINT_WNM_PPOINT_MATCH,
- mWifiConfigManager.matchProviderWithCurrentNetwork(config.FQDN));
- }
- }
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
/**
* Gets the SSID from the WifiConfiguration pointed at by 'mTargetNetworkId'
* This should match the network config framework is attempting to connect to.
*/
private String getTargetSsid() {
- WifiConfiguration currentConfig = mWifiConfigManager.getWifiConfiguration(mTargetNetworkId);
+ WifiConfiguration currentConfig = mWifiConfigManager.getConfiguredNetwork(mTargetNetworkId);
if (currentConfig != null) {
return currentConfig.SSID;
}
@@ -8346,10 +6991,62 @@
}
/**
+ * Send message to WifiP2pServiceImpl.
+ * @return true if message is sent.
+ * false if there is no channel configured for WifiP2pServiceImpl.
+ */
+ private boolean p2pSendMessage(int what) {
+ if (mWifiP2pChannel != null) {
+ mWifiP2pChannel.sendMessage(what);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Send message to WifiP2pServiceImpl with an additional param |arg1|.
+ * @return true if message is sent.
+ * false if there is no channel configured for WifiP2pServiceImpl.
+ */
+ private boolean p2pSendMessage(int what, int arg1) {
+ if (mWifiP2pChannel != null) {
+ mWifiP2pChannel.sendMessage(what, arg1);
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Check if there is any connection request for WiFi network.
* Note, caller of this helper function must acquire mWifiReqCountLock.
*/
private boolean hasConnectionRequests() {
return mConnectionReqCount > 0 || mUntrustedReqCount > 0;
}
+
+ /**
+ * Returns whether CMD_IP_REACHABILITY_LOST events should trigger disconnects.
+ */
+ public boolean getIpReachabilityDisconnectEnabled() {
+ return mIpReachabilityDisconnectEnabled;
+ }
+
+ /**
+ * Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects.
+ */
+ public void setIpReachabilityDisconnectEnabled(boolean enabled) {
+ mIpReachabilityDisconnectEnabled = enabled;
+ }
+
+ /**
+ * Sends a message to initialize the WifiStateMachine.
+ *
+ * @return true if succeeded, false otherwise.
+ */
+ public boolean syncInitialize(AsyncChannel channel) {
+ Message resultMsg = channel.sendMessageSynchronously(CMD_INITIALIZE);
+ boolean result = (resultMsg.arg1 != FAILURE);
+ resultMsg.recycle();
+ return result;
+ }
}
diff --git a/service/java/com/android/server/wifi/WifiStateMachinePrime.java b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
new file mode 100644
index 0000000..2006884
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiStateMachinePrime.java
@@ -0,0 +1,450 @@
+/*
+ * 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.IApInterface;
+import android.net.wifi.IWificond;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+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.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+/**
+ * This class provides the implementation for different WiFi operating modes.
+ *
+ * NOTE: The class is a WIP and is in active development. It is intended to replace the existing
+ * WifiStateMachine.java class when the rearchitecture is complete.
+ */
+public class WifiStateMachinePrime {
+ private static final String TAG = "WifiStateMachinePrime";
+
+ private ModeStateMachine mModeStateMachine;
+
+ private final WifiInjector mWifiInjector;
+ private final Looper mLooper;
+ private final INetworkManagementService mNMService;
+
+ private IWificond mWificond;
+
+ private Queue<WifiConfiguration> mApConfigQueue = new ConcurrentLinkedQueue<>();
+
+ /* The base for wifi message types */
+ static final int BASE = Protocol.BASE_WIFI;
+
+ /* Start the soft access point */
+ static final int CMD_START_AP = BASE + 21;
+ /* Indicates soft ap start failed */
+ static final int CMD_START_AP_FAILURE = BASE + 22;
+ /* Stop the soft access point */
+ static final int CMD_STOP_AP = BASE + 23;
+ /* Soft access point teardown is completed. */
+ static final int CMD_AP_STOPPED = BASE + 24;
+
+ WifiStateMachinePrime(WifiInjector wifiInjector,
+ Looper looper,
+ INetworkManagementService nmService) {
+ mWifiInjector = wifiInjector;
+ mLooper = looper;
+ mNMService = nmService;
+
+ // Clean up existing interfaces in wificond.
+ // This ensures that the framework and wificond are in a consistent state after a framework
+ // restart.
+ try {
+ mWificond = mWifiInjector.makeWificond();
+ if (mWificond != null) {
+ mWificond.tearDownInterfaces();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "wificond died during framework startup");
+ }
+ }
+
+ /**
+ * Method to switch wifi into client mode where connections to configured networks will be
+ * attempted.
+ */
+ public void enterClientMode() {
+ changeMode(ModeStateMachine.CMD_START_CLIENT_MODE);
+ }
+
+ /**
+ * Method to switch wifi into scan only mode where network connection attempts will not be made.
+ *
+ * This mode is utilized by location scans. If wifi is disabled by a user, but they have
+ * previously configured their device to perform location scans, this mode allows wifi to
+ * fulfill the location scan requests but will not be used for connectivity.
+ */
+ public void enterScanOnlyMode() {
+ changeMode(ModeStateMachine.CMD_START_SCAN_ONLY_MODE);
+ }
+
+ /**
+ * Method to enable soft ap for wifi hotspot.
+ *
+ * The WifiConfiguration is generally going to be null to indicate that the
+ * currently saved config in WifiApConfigManager should be used. When the config is
+ * not null, it will be saved in the WifiApConfigManager. This save is performed in the
+ * constructor of SoftApManager.
+ *
+ * @param wifiConfig WifiConfiguration for the hostapd softap
+ */
+ public void enterSoftAPMode(WifiConfiguration wifiConfig) {
+ if (wifiConfig == null) {
+ wifiConfig = new WifiConfiguration();
+ }
+ mApConfigQueue.offer(wifiConfig);
+ changeMode(ModeStateMachine.CMD_START_SOFT_AP_MODE);
+ }
+
+ /**
+ * Method to fully disable wifi.
+ *
+ * This mode will completely shut down wifi and will not perform any network scans.
+ */
+ public void disableWifi() {
+ changeMode(ModeStateMachine.CMD_DISABLE_WIFI);
+ }
+
+ protected String getCurrentMode() {
+ if (mModeStateMachine != null) {
+ return mModeStateMachine.getCurrentMode();
+ }
+ return "WifiDisabledState";
+ }
+
+ private void changeMode(int newMode) {
+ if (mModeStateMachine == null) {
+ if (newMode == ModeStateMachine.CMD_DISABLE_WIFI) {
+ // command is to disable wifi, but it is already disabled.
+ Log.e(TAG, "Received call to disable wifi when it is already disabled.");
+ return;
+ }
+ // state machine was not initialized yet, we must be starting up.
+ mModeStateMachine = new ModeStateMachine();
+ }
+ mModeStateMachine.sendMessage(newMode);
+ }
+
+ private class ModeStateMachine extends StateMachine {
+ // Commands for the state machine.
+ public static final int CMD_START_CLIENT_MODE = 0;
+ public static final int CMD_START_SCAN_ONLY_MODE = 1;
+ public static final int CMD_START_SOFT_AP_MODE = 2;
+ public static final int CMD_DISABLE_WIFI = 3;
+
+ // Create the base modes for WSM.
+ private final State mClientModeState = new ClientModeState();
+ private final State mScanOnlyModeState = new ScanOnlyModeState();
+ private final State mSoftAPModeState = new SoftAPModeState();
+ private final State mWifiDisabledState = new WifiDisabledState();
+
+ // Create the active versions of the modes for WSM.
+ private final State mClientModeActiveState = new ClientModeActiveState();
+ private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState();
+ private final State mSoftAPModeActiveState = new SoftAPModeActiveState();
+
+ ModeStateMachine() {
+ super(TAG, mLooper);
+
+ // CHECKSTYLE:OFF IndentationCheck
+ addState(mClientModeState);
+ addState(mClientModeActiveState, mClientModeState);
+ addState(mScanOnlyModeState);
+ addState(mScanOnlyModeActiveState, mScanOnlyModeState);
+ addState(mSoftAPModeState);
+ addState(mSoftAPModeActiveState, mSoftAPModeState);
+ addState(mWifiDisabledState);
+ // CHECKSTYLE:ON IndentationCheck
+
+ Log.d(TAG, "Starting Wifi in WifiDisabledState");
+ setInitialState(mWifiDisabledState);
+ start();
+ }
+
+ private String getCurrentMode() {
+ return getCurrentState().getName();
+ }
+
+ private boolean checkForAndHandleModeChange(Message message) {
+ switch(message.what) {
+ case ModeStateMachine.CMD_START_CLIENT_MODE:
+ Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode");
+ mModeStateMachine.transitionTo(mClientModeState);
+ break;
+ case ModeStateMachine.CMD_START_SCAN_ONLY_MODE:
+ Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode");
+ mModeStateMachine.transitionTo(mScanOnlyModeState);
+ break;
+ case ModeStateMachine.CMD_START_SOFT_AP_MODE:
+ Log.d(TAG, "Switching from " + getCurrentMode() + " to SoftApMode");
+ mModeStateMachine.transitionTo(mSoftAPModeState);
+ break;
+ case ModeStateMachine.CMD_DISABLE_WIFI:
+ Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled");
+ mModeStateMachine.transitionTo(mWifiDisabledState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ private void tearDownInterfaces() {
+ 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;
+ }
+ return;
+ }
+
+ class ClientModeState extends State {
+ @Override
+ public void enter() {
+ mWificond = mWifiInjector.makeWificond();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (checkForAndHandleModeChange(message)) {
+ return HANDLED;
+ }
+ return NOT_HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ tearDownInterfaces();
+ }
+ }
+
+ class ScanOnlyModeState extends State {
+ @Override
+ public void enter() {
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (checkForAndHandleModeChange(message)) {
+ return HANDLED;
+ }
+ return NOT_HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ // Do not tear down interfaces yet since this mode is not actively controlled or
+ // used in tests at this time.
+ // tearDownInterfaces();
+ }
+ }
+
+ class SoftAPModeState extends State {
+ IApInterface mApInterface = null;
+
+ @Override
+ public void enter() {
+ final Message message = mModeStateMachine.getCurrentMessage();
+ if (message.what != ModeStateMachine.CMD_START_SOFT_AP_MODE) {
+ Log.d(TAG, "Entering SoftAPMode (idle)");
+ return;
+ }
+
+ // Continue with setup since we are changing modes
+ mApInterface = null;
+ mWificond = mWifiInjector.makeWificond();
+ if (mWificond == null) {
+ Log.e(TAG, "Failed to get reference to wificond");
+ writeApConfigDueToStartFailure();
+ mModeStateMachine.sendMessage(CMD_START_AP_FAILURE);
+ return;
+ }
+
+ try {
+ mApInterface = mWificond.createApInterface();
+ } catch (RemoteException e1) { }
+
+ if (mApInterface == null) {
+ Log.e(TAG, "Could not get IApInterface instance from wificond");
+ writeApConfigDueToStartFailure();
+ mModeStateMachine.sendMessage(CMD_START_AP_FAILURE);
+ return;
+ }
+ mModeStateMachine.transitionTo(mSoftAPModeActiveState);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (checkForAndHandleModeChange(message)) {
+ return HANDLED;
+ }
+
+ switch(message.what) {
+ case CMD_START_AP:
+ Log.d(TAG, "Received CMD_START_AP (now invalid message) - dropping");
+ break;
+ case CMD_STOP_AP:
+ // not in active state, nothing to stop.
+ break;
+ case CMD_START_AP_FAILURE:
+ Log.e(TAG, "Failed to start SoftApMode. Wait for next mode command.");
+ break;
+ case CMD_AP_STOPPED:
+ Log.d(TAG, "SoftApModeActiveState stopped. Wait for next mode command.");
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ tearDownInterfaces();
+ }
+
+ protected IApInterface getInterface() {
+ return mApInterface;
+ }
+
+ private void writeApConfigDueToStartFailure() {
+ WifiConfiguration config = mApConfigQueue.poll();
+ if (config != null && config.SSID != null) {
+ // Save valid configs for future calls.
+ mWifiInjector.getWifiApConfigStore().setApConfiguration(config);
+ }
+ }
+ }
+
+ class WifiDisabledState extends State {
+ @Override
+ public void enter() {
+ // make sure everything is torn down
+ Log.d(TAG, "Entering WifiDisabledState");
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ Log.d(TAG, "received a message in WifiDisabledState: " + message);
+ if (checkForAndHandleModeChange(message)) {
+ return HANDLED;
+ }
+ return NOT_HANDLED;
+ }
+
+ }
+
+ class ModeActiveState extends State {
+ ActiveModeManager mActiveModeManager;
+
+ @Override
+ public boolean processMessage(Message message) {
+ // handle messages for changing modes here
+ return NOT_HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ // clean up objects from an active state - check with mode handlers to make sure
+ // they are stopping properly.
+ mActiveModeManager.stop();
+ }
+ }
+
+ class ClientModeActiveState extends ModeActiveState {
+ @Override
+ public void enter() {
+ this.mActiveModeManager = new ClientModeManager();
+ }
+ }
+
+ class ScanOnlyModeActiveState extends ModeActiveState {
+ @Override
+ public void enter() {
+ this.mActiveModeManager = new ScanOnlyModeManager();
+ }
+ }
+
+ class SoftAPModeActiveState extends ModeActiveState {
+ private class SoftApListener implements SoftApManager.Listener {
+ @Override
+ public void onStateChanged(int state, int reason) {
+ if (state == WifiManager.WIFI_AP_STATE_DISABLED) {
+ mModeStateMachine.sendMessage(CMD_AP_STOPPED);
+ } else if (state == WifiManager.WIFI_AP_STATE_FAILED) {
+ mModeStateMachine.sendMessage(CMD_START_AP_FAILURE);
+ }
+ }
+ }
+
+ @Override
+ public void enter() {
+ Log.d(TAG, "Entering SoftApModeActiveState");
+ WifiConfiguration config = mApConfigQueue.poll();
+ if (config != null && config.SSID != null) {
+ Log.d(TAG, "Passing config to SoftApManager! " + config);
+ } else {
+ config = null;
+ }
+
+ this.mActiveModeManager = mWifiInjector.makeSoftApManager(mNMService,
+ new SoftApListener(), ((SoftAPModeState) mSoftAPModeState).getInterface(),
+ config);
+ mActiveModeManager.start();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ case CMD_START_AP:
+ Log.d(TAG, "Received CMD_START_AP when active - invalid message - drop");
+ break;
+ case CMD_STOP_AP:
+ mActiveModeManager.stop();
+ break;
+ case CMD_START_AP_FAILURE:
+ Log.d(TAG, "Failed to start SoftApMode. Return to SoftApMode (inactive).");
+ mModeStateMachine.transitionTo(mSoftAPModeState);
+ break;
+ case CMD_AP_STOPPED:
+ Log.d(TAG, "SoftApModeActiveState stopped."
+ + " Return to SoftApMode (inactive).");
+ mModeStateMachine.transitionTo(mSoftAPModeState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+ } // class ModeStateMachine
+}
diff --git a/service/java/com/android/server/wifi/WifiStateTracker.java b/service/java/com/android/server/wifi/WifiStateTracker.java
new file mode 100644
index 0000000..317aa26
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiStateTracker.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.os.BatteryStats;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.app.IBatteryStats;
+
+/**
+ * This class is used to track WifiState to update BatteryStats
+ */
+public class WifiStateTracker {
+ private static final String TAG = "WifiStateTracker";
+
+ public static final int INVALID = 0;
+ public static final int SCAN_MODE = 1;
+ public static final int DISCONNECTED = 2;
+ public static final int CONNECTED = 3;
+ public static final int SOFT_AP = 4;
+ private int mWifiState;
+ private IBatteryStats mBatteryStats;
+
+ public WifiStateTracker(IBatteryStats stats) {
+ mWifiState = INVALID;
+ mBatteryStats = stats;
+ }
+
+ private void informWifiStateBatteryStats(int state) {
+ try {
+ mBatteryStats.noteWifiState(state, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Battery stats unreachable " + e.getMessage());
+ }
+ }
+
+ /**
+ * Inform the WifiState to this tracker to translate into the
+ * WifiState corresponding to BatteryStats.
+ * @param state state corresponding to the WifiStateMachine state
+ */
+ public void updateState(int state) {
+ int reportState = BatteryStats.WIFI_STATE_OFF;
+ if (state != mWifiState) {
+ switch(state) {
+ case SCAN_MODE:
+ reportState = BatteryStats.WIFI_STATE_OFF_SCANNING;
+ break;
+ case DISCONNECTED:
+ reportState = BatteryStats.WIFI_STATE_ON_DISCONNECTED;
+ break;
+ case CONNECTED:
+ reportState = BatteryStats.WIFI_STATE_ON_CONNECTED_STA;
+ break;
+ case SOFT_AP:
+ reportState = BatteryStats.WIFI_STATE_SOFT_AP;
+ break;
+ case INVALID:
+ mWifiState = INVALID;
+ /* Fall through */
+ default:
+ return;
+ }
+ mWifiState = state;
+ informWifiStateBatteryStats(reportState);
+ }
+ return;
+ }
+}
diff --git a/service/java/com/android/server/wifi/WifiTrafficPoller.java b/service/java/com/android/server/wifi/WifiTrafficPoller.java
index 336e0d7..2efbb2e 100644
--- a/service/java/com/android/server/wifi/WifiTrafficPoller.java
+++ b/service/java/com/android/server/wifi/WifiTrafficPoller.java
@@ -38,12 +38,12 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
-/* Polls for traffic stats and notifies the clients */
-final class WifiTrafficPoller {
+/**
+ * Polls for traffic stats and notifies the clients
+ */
+public 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 +71,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 +112,10 @@
}
void enableVerboseLogging(int verbose) {
- if (verbose > 0 ) {
- DBG = true;
+ if (verbose > 0) {
+ mVerboseLoggingEnabled = true;
} else {
- DBG = false;
+ mVerboseLoggingEnabled = false;
}
}
@@ -126,8 +128,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 +141,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 +155,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 +189,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 +208,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/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
new file mode 100644
index 0000000..88f1898
--- /dev/null
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -0,0 +1,2534 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wifi;
+
+import android.hardware.wifi.V1_0.IWifiApIface;
+import android.hardware.wifi.V1_0.IWifiChip;
+import android.hardware.wifi.V1_0.IWifiChipEventCallback;
+import android.hardware.wifi.V1_0.IWifiIface;
+import android.hardware.wifi.V1_0.IWifiRttController;
+import android.hardware.wifi.V1_0.IWifiRttControllerEventCallback;
+import android.hardware.wifi.V1_0.IWifiStaIface;
+import android.hardware.wifi.V1_0.IWifiStaIfaceEventCallback;
+import android.hardware.wifi.V1_0.IfaceType;
+import android.hardware.wifi.V1_0.RttBw;
+import android.hardware.wifi.V1_0.RttConfig;
+import android.hardware.wifi.V1_0.RttPeerType;
+import android.hardware.wifi.V1_0.RttPreamble;
+import android.hardware.wifi.V1_0.RttResponder;
+import android.hardware.wifi.V1_0.RttResult;
+import android.hardware.wifi.V1_0.RttType;
+import android.hardware.wifi.V1_0.StaBackgroundScanBucketEventReportSchemeMask;
+import android.hardware.wifi.V1_0.StaBackgroundScanBucketParameters;
+import android.hardware.wifi.V1_0.StaBackgroundScanParameters;
+import android.hardware.wifi.V1_0.StaLinkLayerRadioStats;
+import android.hardware.wifi.V1_0.StaLinkLayerStats;
+import android.hardware.wifi.V1_0.StaRoamingConfig;
+import android.hardware.wifi.V1_0.StaRoamingState;
+import android.hardware.wifi.V1_0.StaScanData;
+import android.hardware.wifi.V1_0.StaScanDataFlagMask;
+import android.hardware.wifi.V1_0.StaScanResult;
+import android.hardware.wifi.V1_0.WifiBand;
+import android.hardware.wifi.V1_0.WifiChannelWidthInMhz;
+import android.hardware.wifi.V1_0.WifiDebugHostWakeReasonStats;
+import android.hardware.wifi.V1_0.WifiDebugPacketFateFrameType;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferFlags;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferStatus;
+import android.hardware.wifi.V1_0.WifiDebugRxPacketFate;
+import android.hardware.wifi.V1_0.WifiDebugRxPacketFateReport;
+import android.hardware.wifi.V1_0.WifiDebugTxPacketFate;
+import android.hardware.wifi.V1_0.WifiDebugTxPacketFateReport;
+import android.hardware.wifi.V1_0.WifiInformationElement;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.net.apf.ApfCapabilities;
+import android.net.wifi.RttManager;
+import android.net.wifi.RttManager.ResponderConfig;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiLinkLayerStats;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
+import android.net.wifi.WifiSsid;
+import android.net.wifi.WifiWakeReasonAndCounts;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.MutableBoolean;
+import android.util.MutableInt;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.connectivity.KeepalivePacketData;
+import com.android.server.wifi.util.BitMask;
+import com.android.server.wifi.util.NativeUtil;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Vendor HAL via HIDL
+ */
+public class WifiVendorHal {
+
+ private static final WifiLog sNoLog = new FakeWifiLog();
+
+ /**
+ * Chatty logging should use mVerboseLog
+ */
+ @VisibleForTesting
+ WifiLog mVerboseLog = sNoLog;
+
+ /**
+ * Errors should use mLog
+ */
+ @VisibleForTesting
+ WifiLog mLog = new LogcatLog("WifiVendorHal");
+
+ /**
+ * Enables or disables verbose logging
+ *
+ * @param verbose - with the obvious interpretation
+ */
+ public void enableVerboseLogging(boolean verbose) {
+ synchronized (sLock) {
+ if (verbose) {
+ mVerboseLog = mLog;
+ enter("verbose=true").flush();
+ } else {
+ enter("verbose=false").flush();
+ mVerboseLog = sNoLog;
+ }
+ }
+ }
+
+ /**
+ * Checks for a successful status result.
+ *
+ * Failures are logged to mLog.
+ *
+ * @param status is the WifiStatus generated by a hal call
+ * @return true for success, false for failure
+ */
+ private boolean ok(WifiStatus status) {
+ if (status.code == WifiStatusCode.SUCCESS) return true;
+
+ Thread cur = Thread.currentThread();
+ StackTraceElement[] trace = cur.getStackTrace();
+
+ mLog.err("% failed %")
+ .c(niceMethodName(trace, 3))
+ .c(status.toString())
+ .flush();
+
+ return false;
+ }
+
+ /**
+ * Logs if the argument is false.
+ *
+ * Always returns its argument.
+ */
+ private boolean boolResult(boolean result) {
+ if (mVerboseLog == sNoLog) return result;
+ // Currently only seen if verbose logging is on
+
+ Thread cur = Thread.currentThread();
+ StackTraceElement[] trace = cur.getStackTrace();
+
+ mVerboseLog.err("% returns %")
+ .c(niceMethodName(trace, 3))
+ .c(result)
+ .flush();
+
+ return result;
+ }
+
+ /**
+ * Logs at method entry
+ *
+ * @param format string with % placeholders
+ * @return LogMessage formatter (remember to .flush())
+ */
+ private WifiLog.LogMessage enter(String format) {
+ if (mVerboseLog == sNoLog) return sNoLog.info(format);
+ Thread cur = Thread.currentThread();
+ StackTraceElement[] trace = cur.getStackTrace();
+ return mVerboseLog.trace("% " + format).c(trace[3].getMethodName());
+ }
+
+ /**
+ * Gets the method name and line number from a stack trace.
+ *
+ * Attempts to skip frames created by lambdas to get a human-sensible name.
+ *
+ * @param trace, fo example obtained by Thread.currentThread().getStackTrace()
+ * @param start frame number to log, typically 3
+ * @return string cotaining the method name and line number
+ */
+ private static String niceMethodName(StackTraceElement[] trace, int start) {
+ if (start >= trace.length) return "";
+ StackTraceElement s = trace[start];
+ String name = s.getMethodName();
+ if (name.contains("lambda$")) {
+ // Try to find a friendlier method name
+ String myFile = s.getFileName();
+ if (myFile != null) {
+ for (int i = start + 1; i < trace.length; i++) {
+ if (myFile.equals(trace[i].getFileName())) {
+ name = trace[i].getMethodName();
+ break;
+ }
+ }
+ }
+ }
+ return (name + "(l." + s.getLineNumber() + ")");
+ }
+
+ // Vendor HAL HIDL interface objects.
+ private IWifiChip mIWifiChip;
+ private IWifiStaIface mIWifiStaIface;
+ private IWifiApIface mIWifiApIface;
+ private IWifiRttController mIWifiRttController;
+ private final HalDeviceManager mHalDeviceManager;
+ private final HalDeviceManagerStatusListener mHalDeviceManagerStatusCallbacks;
+ private final IWifiStaIfaceEventCallback mIWifiStaIfaceEventCallback;
+ private final IWifiChipEventCallback mIWifiChipEventCallback;
+ private final RttEventCallback mRttEventCallback;
+
+ // Plumbing for event handling.
+ //
+ // Being final fields, they can be accessed without synchronization under
+ // some reasonable assumptions. See
+ // https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5
+ private final Looper mLooper;
+ private final Handler mHalEventHandler;
+
+ public WifiVendorHal(HalDeviceManager halDeviceManager,
+ Looper looper) {
+ mHalDeviceManager = halDeviceManager;
+ mLooper = looper;
+ mHalEventHandler = new Handler(looper);
+ mHalDeviceManagerStatusCallbacks = new HalDeviceManagerStatusListener();
+ mIWifiStaIfaceEventCallback = new StaIfaceEventCallback();
+ mIWifiChipEventCallback = new ChipEventCallback();
+ mRttEventCallback = new RttEventCallback();
+ }
+
+ // TODO(mplass): figure out where we need locking in hidl world. b/33383725
+ public static final Object sLock = new Object();
+
+ private void handleRemoteException(RemoteException e) {
+ String methodName = niceMethodName(Thread.currentThread().getStackTrace(), 3);
+ mVerboseLog.err("% RemoteException in HIDL call %").c(methodName).c(e.toString()).flush();
+ clearState();
+ }
+
+ private WifiNative.VendorHalDeathEventHandler mDeathEventHandler;
+
+ /**
+ * Initialize the Hal device manager and register for status callbacks.
+ *
+ * @param handler Handler to notify if the vendor HAL dies.
+ * @return true on success, false otherwise.
+ */
+ public boolean initialize(WifiNative.VendorHalDeathEventHandler handler) {
+ synchronized (sLock) {
+ mHalDeviceManager.initialize();
+ mHalDeviceManager.registerStatusListener(mHalDeviceManagerStatusCallbacks, mLooper);
+ mDeathEventHandler = handler;
+ return true;
+ }
+ }
+
+ /**
+ * Returns whether the vendor HAL is supported on this device or not.
+ */
+ public boolean isVendorHalSupported() {
+ synchronized (sLock) {
+ return mHalDeviceManager.isSupported();
+ }
+ }
+
+ /**
+ * Bring up the HIDL Vendor HAL and configure for AP (Access Point) mode
+ *
+ * @return true for success
+ */
+ public boolean startVendorHalAp() {
+ return startVendorHal(AP_MODE);
+ }
+
+ /**
+ * Bring up the HIDL Vendor HAL and configure for STA (Station) mode
+ *
+ * @return true for success
+ */
+ public boolean startVendorHalSta() {
+ return startVendorHal(STA_MODE);
+ }
+
+ public static final boolean STA_MODE = true;
+ public static final boolean AP_MODE = false;
+
+ /**
+ * Bring up the HIDL Vendor HAL and configure for STA mode or AP mode.
+ *
+ * @param isStaMode true to start HAL in STA mode, false to start in AP mode.
+ */
+ public boolean startVendorHal(boolean isStaMode) {
+ synchronized (sLock) {
+ if (mIWifiStaIface != null) return boolResult(false);
+ if (mIWifiApIface != null) return boolResult(false);
+ if (!mHalDeviceManager.start()) {
+ return startFailedTo("start the vendor HAL");
+ }
+ IWifiIface iface;
+ if (isStaMode) {
+ mIWifiStaIface = mHalDeviceManager.createStaIface(null, null);
+ if (mIWifiStaIface == null) {
+ return startFailedTo("create STA Iface");
+ }
+ iface = (IWifiIface) mIWifiStaIface;
+ if (!registerStaIfaceCallback()) {
+ return startFailedTo("register sta iface callback");
+ }
+ mIWifiRttController = mHalDeviceManager.createRttController(iface);
+ if (mIWifiRttController == null) {
+ return startFailedTo("create RTT controller");
+ }
+ if (!registerRttEventCallback()) {
+ return startFailedTo("register RTT iface callback");
+ }
+ enableLinkLayerStats();
+ } else {
+ mIWifiApIface = mHalDeviceManager.createApIface(null, null);
+ if (mIWifiApIface == null) {
+ return startFailedTo("create AP Iface");
+ }
+ iface = (IWifiIface) mIWifiApIface;
+ }
+ mIWifiChip = mHalDeviceManager.getChip(iface);
+ if (mIWifiChip == null) {
+ return startFailedTo("get the chip created for the Iface");
+ }
+ if (!registerChipCallback()) {
+ return startFailedTo("register chip callback");
+ }
+ mLog.i("Vendor Hal started successfully");
+ return true;
+ }
+ }
+
+ /**
+ * Logs a message and cleans up after a failing start attempt
+ *
+ * The lock should be held.
+ * @param message describes what was being attempted
+ * @return false
+ */
+ private boolean startFailedTo(String message) {
+ mVerboseLog.err("Failed to %. Vendor Hal start failed").c(message).flush();
+ mHalDeviceManager.stop();
+ clearState();
+ return false;
+ }
+
+ /**
+ * Registers the sta iface callback.
+ */
+ private boolean registerStaIfaceCallback() {
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return boolResult(false);
+ if (mIWifiStaIfaceEventCallback == null) return boolResult(false);
+ try {
+ WifiStatus status =
+ mIWifiStaIface.registerEventCallback(mIWifiStaIfaceEventCallback);
+ return ok(status);
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Registers the sta iface callback.
+ */
+ private boolean registerChipCallback() {
+ synchronized (sLock) {
+ if (mIWifiChip == null) return boolResult(false);
+ if (mIWifiChipEventCallback == null) return boolResult(false);
+ try {
+ WifiStatus status = mIWifiChip.registerEventCallback(mIWifiChipEventCallback);
+ return ok(status);
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Registers RTT event callback. Returns whether the registration is successful.
+ */
+ private boolean registerRttEventCallback() {
+ synchronized (sLock) {
+ if (mIWifiRttController == null) return boolResult(false);
+ try {
+ WifiStatus status = mIWifiRttController.registerEventCallback(mRttEventCallback);
+ return ok(status);
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Stops the HAL
+ */
+ public void stopVendorHal() {
+ synchronized (sLock) {
+ mHalDeviceManager.stop();
+ clearState();
+ mLog.i("Vendor Hal stopped");
+ }
+ }
+
+ /**
+ * Clears the state associated with a started Iface
+ *
+ * Caller should hold the lock.
+ */
+ private void clearState() {
+ mIWifiChip = null;
+ mIWifiStaIface = null;
+ mIWifiApIface = null;
+ mIWifiRttController = null;
+ mDriverDescription = null;
+ mFirmwareDescription = null;
+ mChannelsForBandSupport = null;
+ }
+
+ /**
+ * Tests whether the HAL is running or not
+ */
+ public boolean isHalStarted() {
+ // For external use only. Methods in this class should test for null directly.
+ synchronized (sLock) {
+ return (mIWifiStaIface != null || mIWifiApIface != null);
+ }
+ }
+
+ /**
+ * Gets the scan capabilities
+ *
+ * @param capabilities object to be filled in
+ * @return true for success, false for failure
+ */
+ public boolean getBgScanCapabilities(WifiNative.ScanCapabilities capabilities) {
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return boolResult(false);
+ try {
+ MutableBoolean ans = new MutableBoolean(false);
+ WifiNative.ScanCapabilities out = capabilities;
+ mIWifiStaIface.getBackgroundScanCapabilities((status, cap) -> {
+ if (!ok(status)) return;
+ mVerboseLog.info("scan capabilities %").c(cap.toString()).flush();
+ out.max_scan_cache_size = cap.maxCacheSize;
+ out.max_ap_cache_per_scan = cap.maxApCachePerScan;
+ out.max_scan_buckets = cap.maxBuckets;
+ out.max_rssi_sample_size = 0;
+ out.max_scan_reporting_threshold = cap.maxReportingThreshold;
+ ans.value = true;
+ }
+ );
+ return ans.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Holds the current background scan state, to implement pause and restart
+ */
+ @VisibleForTesting
+ class CurrentBackgroundScan {
+ public int cmdId;
+ public StaBackgroundScanParameters param;
+ public WifiNative.ScanEventHandler eventHandler = null;
+ public boolean paused = false;
+ public WifiScanner.ScanData[] latestScanResults = null;
+
+ CurrentBackgroundScan(int id, WifiNative.ScanSettings settings) {
+ cmdId = id;
+ param = new StaBackgroundScanParameters();
+ param.basePeriodInMs = settings.base_period_ms;
+ param.maxApPerScan = settings.max_ap_per_scan;
+ param.reportThresholdPercent = settings.report_threshold_percent;
+ param.reportThresholdNumScans = settings.report_threshold_num_scans;
+ if (settings.buckets != null) {
+ for (WifiNative.BucketSettings bs : settings.buckets) {
+ param.buckets.add(makeStaBackgroundScanBucketParametersFromBucketSettings(bs));
+ }
+ }
+ }
+ }
+
+ /**
+ * Makes the Hal flavor of WifiNative.BucketSettings
+ *
+ * @param bs WifiNative.BucketSettings
+ * @return Hal flavor of bs
+ * @throws IllegalArgumentException if band value is not recognized
+ */
+ private StaBackgroundScanBucketParameters
+ makeStaBackgroundScanBucketParametersFromBucketSettings(WifiNative.BucketSettings bs) {
+ StaBackgroundScanBucketParameters pa = new StaBackgroundScanBucketParameters();
+ pa.bucketIdx = bs.bucket;
+ pa.band = makeWifiBandFromFrameworkBand(bs.band);
+ if (bs.channels != null) {
+ for (WifiNative.ChannelSettings cs : bs.channels) {
+ pa.frequencies.add(cs.frequency);
+ }
+ }
+ pa.periodInMs = bs.period_ms;
+ pa.eventReportScheme = makeReportSchemeFromBucketSettingsReportEvents(bs.report_events);
+ pa.exponentialMaxPeriodInMs = bs.max_period_ms;
+ // Although HAL API allows configurable base value for the truncated
+ // exponential back off scan. Native API and above support only
+ // truncated binary exponential back off scan.
+ // Hard code value of base to 2 here.
+ pa.exponentialBase = 2;
+ pa.exponentialStepCount = bs.step_count;
+ return pa;
+ }
+
+ /**
+ * Makes the Hal flavor of WifiScanner's band indication
+ *
+ * @param frameworkBand one of WifiScanner.WIFI_BAND_*
+ * @return A WifiBand value
+ * @throws IllegalArgumentException if frameworkBand is not recognized
+ */
+ private int makeWifiBandFromFrameworkBand(int frameworkBand) {
+ switch (frameworkBand) {
+ case WifiScanner.WIFI_BAND_UNSPECIFIED:
+ return WifiBand.BAND_UNSPECIFIED;
+ case WifiScanner.WIFI_BAND_24_GHZ:
+ return WifiBand.BAND_24GHZ;
+ case WifiScanner.WIFI_BAND_5_GHZ:
+ return WifiBand.BAND_5GHZ;
+ case WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY:
+ return WifiBand.BAND_5GHZ_DFS;
+ case WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS:
+ return WifiBand.BAND_5GHZ_WITH_DFS;
+ case WifiScanner.WIFI_BAND_BOTH:
+ return WifiBand.BAND_24GHZ_5GHZ;
+ case WifiScanner.WIFI_BAND_BOTH_WITH_DFS:
+ return WifiBand.BAND_24GHZ_5GHZ_WITH_DFS;
+ default:
+ throw new IllegalArgumentException("bad band " + frameworkBand);
+ }
+ }
+
+ /**
+ * Makes the Hal flavor of WifiScanner's report event mask
+ *
+ * @param reportUnderscoreEvents is logical OR of WifiScanner.REPORT_EVENT_* values
+ * @return Corresponding StaBackgroundScanBucketEventReportSchemeMask value
+ * @throws IllegalArgumentException if a mask bit is not recognized
+ */
+ private int makeReportSchemeFromBucketSettingsReportEvents(int reportUnderscoreEvents) {
+ int ans = 0;
+ BitMask in = new BitMask(reportUnderscoreEvents);
+ if (in.testAndClear(WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN)) {
+ ans |= StaBackgroundScanBucketEventReportSchemeMask.EACH_SCAN;
+ }
+ if (in.testAndClear(WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)) {
+ ans |= StaBackgroundScanBucketEventReportSchemeMask.FULL_RESULTS;
+ }
+ if (in.testAndClear(WifiScanner.REPORT_EVENT_NO_BATCH)) {
+ ans |= StaBackgroundScanBucketEventReportSchemeMask.NO_BATCH;
+ }
+ if (in.value != 0) throw new IllegalArgumentException("bad " + reportUnderscoreEvents);
+ return ans;
+ }
+
+ private int mLastScanCmdId; // For assigning cmdIds to scans
+
+ @VisibleForTesting
+ CurrentBackgroundScan mScan = null;
+
+ /**
+ * Starts a background scan
+ *
+ * Any ongoing scan will be stopped first
+ *
+ * @param settings to control the scan
+ * @param eventHandler to call with the results
+ * @return true for success
+ */
+ public boolean startBgScan(WifiNative.ScanSettings settings,
+ WifiNative.ScanEventHandler eventHandler) {
+ WifiStatus status;
+ if (eventHandler == null) return boolResult(false);
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return boolResult(false);
+ try {
+ if (mScan != null && !mScan.paused) {
+ ok(mIWifiStaIface.stopBackgroundScan(mScan.cmdId));
+ mScan = null;
+ }
+ mLastScanCmdId = (mLastScanCmdId % 9) + 1; // cycle through non-zero single digits
+ CurrentBackgroundScan scan = new CurrentBackgroundScan(mLastScanCmdId, settings);
+ status = mIWifiStaIface.startBackgroundScan(scan.cmdId, scan.param);
+ if (!ok(status)) return false;
+ scan.eventHandler = eventHandler;
+ mScan = scan;
+ return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+
+ /**
+ * Stops any ongoing backgound scan
+ */
+ public void stopBgScan() {
+ WifiStatus status;
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return;
+ try {
+ if (mScan != null) {
+ ok(mIWifiStaIface.stopBackgroundScan(mScan.cmdId));
+ mScan = null;
+ }
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ }
+ }
+ }
+
+ /**
+ * Pauses an ongoing backgound scan
+ */
+ public void pauseBgScan() {
+ WifiStatus status;
+ synchronized (sLock) {
+ try {
+ if (mIWifiStaIface == null) return;
+ if (mScan != null && !mScan.paused) {
+ status = mIWifiStaIface.stopBackgroundScan(mScan.cmdId);
+ if (!ok(status)) return;
+ mScan.paused = true;
+ }
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ }
+ }
+ }
+
+ /**
+ * Restarts a paused background scan
+ */
+ public void restartBgScan() {
+ WifiStatus status;
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return;
+ try {
+ if (mScan != null && mScan.paused) {
+ status = mIWifiStaIface.startBackgroundScan(mScan.cmdId, mScan.param);
+ if (!ok(status)) return;
+ mScan.paused = false;
+ }
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ }
+ }
+ }
+
+ /**
+ * Gets the latest scan results received from the HIDL interface callback.
+ * TODO(b/35754840): This hop to fetch scan results after callback is unnecessary. Refactor
+ * WifiScanner to use the scan results from the callback.
+ */
+ public WifiScanner.ScanData[] getBgScanResults() {
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return null;
+ if (mScan == null) return null;
+ return mScan.latestScanResults;
+ }
+ }
+
+ /**
+ * Get the link layer statistics
+ *
+ * Note - we always enable link layer stats on a STA interface.
+ *
+ * @return the statistics, or null if unable to do so
+ */
+ public WifiLinkLayerStats getWifiLinkLayerStats() {
+ class AnswerBox {
+ public StaLinkLayerStats value = null;
+ }
+ AnswerBox answer = new AnswerBox();
+ synchronized (sLock) {
+ try {
+ if (mIWifiStaIface == null) return null;
+ mIWifiStaIface.getLinkLayerStats((status, stats) -> {
+ if (!ok(status)) return;
+ answer.value = stats;
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return null;
+ }
+ }
+ WifiLinkLayerStats stats = frameworkFromHalLinkLayerStats(answer.value);
+ return stats;
+ }
+
+ /**
+ * Makes the framework version of link layer stats from the hal version.
+ */
+ @VisibleForTesting
+ static WifiLinkLayerStats frameworkFromHalLinkLayerStats(StaLinkLayerStats stats) {
+ if (stats == null) return null;
+ WifiLinkLayerStats out = new WifiLinkLayerStats();
+ // unpopulated: out.status, out.SSID, out.BSSID
+ out.beacon_rx = stats.iface.beaconRx;
+ out.rssi_mgmt = stats.iface.avgRssiMgmt;
+ // Statistics are broken out by Wireless Multimedia Extensions categories
+ // WME Best Effort Access Category
+ out.rxmpdu_be = stats.iface.wmeBePktStats.rxMpdu;
+ out.txmpdu_be = stats.iface.wmeBePktStats.txMpdu;
+ out.lostmpdu_be = stats.iface.wmeBePktStats.lostMpdu;
+ out.retries_be = stats.iface.wmeBePktStats.retries;
+ // WME Background Access Category
+ out.rxmpdu_bk = stats.iface.wmeBkPktStats.rxMpdu;
+ out.txmpdu_bk = stats.iface.wmeBkPktStats.txMpdu;
+ out.lostmpdu_bk = stats.iface.wmeBkPktStats.lostMpdu;
+ out.retries_bk = stats.iface.wmeBkPktStats.retries;
+ // WME Video Access Category
+ out.rxmpdu_vi = stats.iface.wmeViPktStats.rxMpdu;
+ out.txmpdu_vi = stats.iface.wmeViPktStats.txMpdu;
+ out.lostmpdu_vi = stats.iface.wmeViPktStats.lostMpdu;
+ out.retries_vi = stats.iface.wmeViPktStats.retries;
+ // WME Voice Access Category
+ out.rxmpdu_vo = stats.iface.wmeVoPktStats.rxMpdu;
+ out.txmpdu_vo = stats.iface.wmeVoPktStats.txMpdu;
+ out.lostmpdu_vo = stats.iface.wmeVoPktStats.lostMpdu;
+ out.retries_vo = stats.iface.wmeVoPktStats.retries;
+ // TODO(b/36176141): Figure out how to coalesce this info for multi radio devices.
+ if (stats.radios.size() > 0) {
+ StaLinkLayerRadioStats radioStats = stats.radios.get(0);
+ out.on_time = radioStats.onTimeInMs;
+ out.tx_time = radioStats.txTimeInMs;
+ out.tx_time_per_level = new int[radioStats.txTimeInMsPerLevel.size()];
+ for (int i = 0; i < out.tx_time_per_level.length; i++) {
+ out.tx_time_per_level[i] = radioStats.txTimeInMsPerLevel.get(i);
+ }
+ out.rx_time = radioStats.rxTimeInMs;
+ out.on_time_scan = radioStats.onTimeInMsForScan;
+ }
+ // unused: stats.timeStampInMs;
+ return out;
+ }
+
+ @VisibleForTesting
+ boolean mLinkLayerStatsDebug = false; // Passed to Hal
+
+ /**
+ * Enables the linkLayerStats in the Hal.
+ *
+ * This is called unconditionally whenever we create a STA interface.
+ */
+ private void enableLinkLayerStats() {
+ synchronized (sLock) {
+ try {
+ WifiStatus status;
+ status = mIWifiStaIface.enableLinkLayerStatsCollection(mLinkLayerStatsDebug);
+ if (!ok(status)) {
+ mLog.e("unable to enable link layer stats collection");
+ }
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ }
+ }
+ }
+
+ /**
+ * Translation table used by getSupportedFeatureSet for translating IWifiStaIface caps
+ */
+ private static final int[][] sFeatureCapabilityTranslation = {
+ {WifiManager.WIFI_FEATURE_INFRA_5G,
+ IWifiStaIface.StaIfaceCapabilityMask.STA_5G
+ },
+ {WifiManager.WIFI_FEATURE_PASSPOINT,
+ IWifiStaIface.StaIfaceCapabilityMask.HOTSPOT
+ },
+ {WifiManager.WIFI_FEATURE_SCANNER,
+ IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN,
+ },
+ {WifiManager.WIFI_FEATURE_PNO,
+ IWifiStaIface.StaIfaceCapabilityMask.PNO
+ },
+ {WifiManager.WIFI_FEATURE_TDLS,
+ IWifiStaIface.StaIfaceCapabilityMask.TDLS
+ },
+ {WifiManager.WIFI_FEATURE_TDLS_OFFCHANNEL,
+ IWifiStaIface.StaIfaceCapabilityMask.TDLS_OFFCHANNEL
+ },
+ {WifiManager.WIFI_FEATURE_LINK_LAYER_STATS,
+ IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS
+ },
+ {WifiManager.WIFI_FEATURE_RSSI_MONITOR,
+ IWifiStaIface.StaIfaceCapabilityMask.RSSI_MONITOR
+ },
+ {WifiManager.WIFI_FEATURE_MKEEP_ALIVE,
+ IWifiStaIface.StaIfaceCapabilityMask.KEEP_ALIVE
+ },
+ {WifiManager.WIFI_FEATURE_CONFIG_NDO,
+ IWifiStaIface.StaIfaceCapabilityMask.ND_OFFLOAD
+ },
+ {WifiManager.WIFI_FEATURE_CONTROL_ROAMING,
+ IWifiStaIface.StaIfaceCapabilityMask.CONTROL_ROAMING
+ },
+ {WifiManager.WIFI_FEATURE_IE_WHITELIST,
+ IWifiStaIface.StaIfaceCapabilityMask.PROBE_IE_WHITELIST
+ },
+ {WifiManager.WIFI_FEATURE_SCAN_RAND,
+ IWifiStaIface.StaIfaceCapabilityMask.SCAN_RAND
+ },
+ };
+
+ /**
+ * Feature bit mask translation for STAs
+ *
+ * @param capabilities bitmask defined IWifiStaIface.StaIfaceCapabilityMask
+ * @return bitmask defined by WifiManager.WIFI_FEATURE_*
+ */
+ @VisibleForTesting
+ int wifiFeatureMaskFromStaCapabilities(int capabilities) {
+ int features = 0;
+ for (int i = 0; i < sFeatureCapabilityTranslation.length; i++) {
+ if ((capabilities & sFeatureCapabilityTranslation[i][1]) != 0) {
+ features |= sFeatureCapabilityTranslation[i][0];
+ }
+ }
+ return features;
+ }
+
+ /**
+ * Get the supported features
+ *
+ * The result may differ depending on the mode (STA or AP)
+ *
+ * @return bitmask defined by WifiManager.WIFI_FEATURE_*
+ */
+ public int getSupportedFeatureSet() {
+ int featureSet = 0;
+ try {
+ final MutableInt feat = new MutableInt(0);
+ synchronized (sLock) {
+ if (mIWifiStaIface != null) {
+ mIWifiStaIface.getCapabilities((status, capabilities) -> {
+ if (!ok(status)) return;
+ feat.value = wifiFeatureMaskFromStaCapabilities(capabilities);
+ });
+ }
+ }
+ featureSet = feat.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return 0;
+ }
+
+ Set<Integer> supportedIfaceTypes = mHalDeviceManager.getSupportedIfaceTypes();
+ if (supportedIfaceTypes.contains(IfaceType.STA)) {
+ featureSet |= WifiManager.WIFI_FEATURE_INFRA;
+ }
+ if (supportedIfaceTypes.contains(IfaceType.AP)) {
+ featureSet |= WifiManager.WIFI_FEATURE_MOBILE_HOTSPOT;
+ }
+ if (supportedIfaceTypes.contains(IfaceType.P2P)) {
+ featureSet |= WifiManager.WIFI_FEATURE_P2P;
+ }
+ if (supportedIfaceTypes.contains(IfaceType.NAN)) {
+ featureSet |= WifiManager.WIFI_FEATURE_AWARE;
+ }
+
+ return featureSet;
+ }
+
+ /* RTT related commands/events */
+
+ /**
+ * RTT (Round Trip Time) measurement capabilities of the device.
+ */
+ public RttManager.RttCapabilities getRttCapabilities() {
+ class AnswerBox {
+ public RttManager.RttCapabilities value = null;
+ }
+ synchronized (sLock) {
+ if (mIWifiRttController == null) return null;
+ try {
+ AnswerBox box = new AnswerBox();
+ mIWifiRttController.getCapabilities((status, capabilities) -> {
+ if (!ok(status)) return;
+ mVerboseLog.info("rtt capabilites %").c(capabilities.toString()).flush();
+ RttManager.RttCapabilities ans = new RttManager.RttCapabilities();
+ ans.oneSidedRttSupported = capabilities.rttOneSidedSupported;
+ ans.twoSided11McRttSupported = capabilities.rttFtmSupported;
+ ans.lciSupported = capabilities.lciSupported;
+ ans.lcrSupported = capabilities.lcrSupported;
+ ans.preambleSupported = frameworkPreambleFromHalPreamble(
+ capabilities.preambleSupport);
+ ans.bwSupported = frameworkBwFromHalBw(capabilities.bwSupport);
+ ans.responderSupported = capabilities.responderSupported;
+ ans.secureRttSupported = false;
+ ans.mcVersion = ((int) capabilities.mcVersion) & 0xff;
+ box.value = ans;
+ });
+ return box.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return null;
+ }
+ }
+ }
+
+ private int mRttCmdIdNext = 1; // used to generate new command ids
+ private int mRttCmdId; // id of currently active request
+ // Event handler for current active RTT request.
+ private WifiNative.RttEventHandler mRttEventHandler;
+
+ /**
+ * Receives a callback from the Hal and passes it along to our client using RttEventHandler
+ */
+ private class RttEventCallback extends IWifiRttControllerEventCallback.Stub {
+
+ @Override
+ public void onResults(int cmdId, java.util.ArrayList<RttResult> results) {
+ WifiNative.RttEventHandler eventHandler;
+ synchronized (sLock) {
+ if (cmdId != mRttCmdId || mRttEventHandler == null) return;
+ eventHandler = mRttEventHandler;
+ // Reset the command id for RTT operations in WifiVendorHal.
+ WifiVendorHal.this.mRttCmdId = 0;
+ }
+ RttManager.RttResult[] rtt = new RttManager.RttResult[results.size()];
+ for (int i = 0; i < rtt.length; i++) {
+ rtt[i] = frameworkRttResultFromHalRttResult(results.get(i));
+ }
+ eventHandler.onRttResults(rtt);
+ }
+ }
+
+ /**
+ * Converts a Hal RttResult to a RttManager.RttResult
+ */
+ @VisibleForTesting
+ static RttManager.RttResult frameworkRttResultFromHalRttResult(RttResult result) {
+ RttManager.RttResult ans = new RttManager.RttResult();
+ ans.bssid = NativeUtil.macAddressFromByteArray(result.addr);
+ ans.burstNumber = result.burstNum;
+ ans.measurementFrameNumber = result.measurementNumber;
+ ans.successMeasurementFrameNumber = result.successNumber;
+ ans.frameNumberPerBurstPeer = result.numberPerBurstPeer;
+ ans.status = result.status; //TODO(b/35138520) - don't assume identity translation
+ ans.retryAfterDuration = result.retryAfterDuration;
+ ans.measurementType = result.type;
+ ans.rssi = result.rssi;
+ ans.rssiSpread = result.rssiSpread;
+ //TODO(b/35138520) Fix HAL and framework to use the same units
+ ans.txRate = result.txRate.bitRateInKbps;
+ ans.rxRate = result.rxRate.bitRateInKbps;
+ ans.rtt = result.rtt;
+ ans.rttStandardDeviation = result.rttSd;
+ ans.rttSpread = result.rttSpread;
+ //TODO(b/35138520) These divide-by-10s were in the legacy Hal
+ ans.distance = result.distanceInMm / 10; // Convert cm to mm
+ ans.distanceStandardDeviation = result.distanceSdInMm / 10; // Convert cm to mm
+ ans.distanceSpread = result.distanceSpreadInMm / 10;
+
+ ans.ts = result.timeStampInUs;
+ ans.burstDuration = result.burstDurationInMs;
+ ans.negotiatedBurstNum = result.negotiatedBurstNum;
+ ans.LCI = ieFromHal(result.lci);
+ ans.LCR = ieFromHal(result.lcr);
+ ans.secure = false; // Not present in HIDL HAL
+ return ans;
+ }
+
+ /**
+ * Convert a Hal WifiInformationElement to its RttManager equivalent
+ */
+ @VisibleForTesting
+ static RttManager.WifiInformationElement ieFromHal(
+ android.hardware.wifi.V1_0.WifiInformationElement ie) {
+ if (ie == null) return null;
+ RttManager.WifiInformationElement ans = new RttManager.WifiInformationElement();
+ ans.id = ie.id;
+ ans.data = NativeUtil.byteArrayFromArrayList(ie.data);
+ return ans;
+ }
+
+ @VisibleForTesting
+ static RttConfig halRttConfigFromFrameworkRttParams(RttManager.RttParams params) {
+ RttConfig rttConfig = new RttConfig();
+ if (params.bssid != null) {
+ byte[] addr = NativeUtil.macAddressToByteArray(params.bssid);
+ for (int i = 0; i < rttConfig.addr.length; i++) {
+ rttConfig.addr[i] = addr[i];
+ }
+ }
+ rttConfig.type = halRttTypeFromFrameworkRttType(params.requestType);
+ rttConfig.peer = halPeerFromFrameworkPeer(params.deviceType);
+ rttConfig.channel.width = halChannelWidthFromFrameworkChannelWidth(params.channelWidth);
+ rttConfig.channel.centerFreq = params.frequency;
+ rttConfig.channel.centerFreq0 = params.centerFreq0;
+ rttConfig.channel.centerFreq1 = params.centerFreq1;
+ rttConfig.burstPeriod = params.interval; // In 100ms units, 0 means no specific
+ rttConfig.numBurst = params.numberBurst;
+ rttConfig.numFramesPerBurst = params.numSamplesPerBurst;
+ rttConfig.numRetriesPerRttFrame = params.numRetriesPerMeasurementFrame;
+ rttConfig.numRetriesPerFtmr = params.numRetriesPerFTMR;
+ rttConfig.mustRequestLci = params.LCIRequest;
+ rttConfig.mustRequestLcr = params.LCRRequest;
+ rttConfig.burstDuration = params.burstTimeout;
+ rttConfig.preamble = halPreambleFromFrameworkPreamble(params.preamble);
+ rttConfig.bw = halBwFromFrameworkBw(params.bandwidth);
+ return rttConfig;
+ }
+
+ @VisibleForTesting
+ static int halRttTypeFromFrameworkRttType(int frameworkRttType) {
+ switch (frameworkRttType) {
+ case RttManager.RTT_TYPE_ONE_SIDED:
+ return RttType.ONE_SIDED;
+ case RttManager.RTT_TYPE_TWO_SIDED:
+ return RttType.TWO_SIDED;
+ default:
+ throw new IllegalArgumentException("bad " + frameworkRttType);
+ }
+ }
+
+ @VisibleForTesting
+ static int frameworkRttTypeFromHalRttType(int halType) {
+ switch (halType) {
+ case RttType.ONE_SIDED:
+ return RttManager.RTT_TYPE_ONE_SIDED;
+ case RttType.TWO_SIDED:
+ return RttManager.RTT_TYPE_TWO_SIDED;
+ default:
+ throw new IllegalArgumentException("bad " + halType);
+ }
+ }
+
+ @VisibleForTesting
+ static int halPeerFromFrameworkPeer(int frameworkPeer) {
+ switch (frameworkPeer) {
+ case RttManager.RTT_PEER_TYPE_AP:
+ return RttPeerType.AP;
+ case RttManager.RTT_PEER_TYPE_STA:
+ return RttPeerType.STA;
+ case RttManager.RTT_PEER_P2P_GO:
+ return RttPeerType.P2P_GO;
+ case RttManager.RTT_PEER_P2P_CLIENT:
+ return RttPeerType.P2P_CLIENT;
+ case RttManager.RTT_PEER_NAN:
+ return RttPeerType.NAN;
+ default:
+ throw new IllegalArgumentException("bad " + frameworkPeer);
+ }
+ }
+
+ @VisibleForTesting
+ static int frameworkPeerFromHalPeer(int halPeer) {
+ switch (halPeer) {
+ case RttPeerType.AP:
+ return RttManager.RTT_PEER_TYPE_AP;
+ case RttPeerType.STA:
+ return RttManager.RTT_PEER_TYPE_STA;
+ case RttPeerType.P2P_GO:
+ return RttManager.RTT_PEER_P2P_GO;
+ case RttPeerType.P2P_CLIENT:
+ return RttManager.RTT_PEER_P2P_CLIENT;
+ case RttPeerType.NAN:
+ return RttManager.RTT_PEER_NAN;
+ default:
+ throw new IllegalArgumentException("bad " + halPeer);
+
+ }
+ }
+
+ @VisibleForTesting
+ static int halChannelWidthFromFrameworkChannelWidth(int frameworkChannelWidth) {
+ switch (frameworkChannelWidth) {
+ case ScanResult.CHANNEL_WIDTH_20MHZ:
+ return WifiChannelWidthInMhz.WIDTH_20;
+ case ScanResult.CHANNEL_WIDTH_40MHZ:
+ return WifiChannelWidthInMhz.WIDTH_40;
+ case ScanResult.CHANNEL_WIDTH_80MHZ:
+ return WifiChannelWidthInMhz.WIDTH_80;
+ case ScanResult.CHANNEL_WIDTH_160MHZ:
+ return WifiChannelWidthInMhz.WIDTH_160;
+ case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ:
+ return WifiChannelWidthInMhz.WIDTH_80P80;
+ default:
+ throw new IllegalArgumentException("bad " + frameworkChannelWidth);
+ }
+ }
+
+ @VisibleForTesting
+ static int frameworkChannelWidthFromHalChannelWidth(int halChannelWidth) {
+ switch (halChannelWidth) {
+ case WifiChannelWidthInMhz.WIDTH_20:
+ return ScanResult.CHANNEL_WIDTH_20MHZ;
+ case WifiChannelWidthInMhz.WIDTH_40:
+ return ScanResult.CHANNEL_WIDTH_40MHZ;
+ case WifiChannelWidthInMhz.WIDTH_80:
+ return ScanResult.CHANNEL_WIDTH_80MHZ;
+ case WifiChannelWidthInMhz.WIDTH_160:
+ return ScanResult.CHANNEL_WIDTH_160MHZ;
+ case WifiChannelWidthInMhz.WIDTH_80P80:
+ return ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ;
+ default:
+ throw new IllegalArgumentException("bad " + halChannelWidth);
+ }
+ }
+
+ @VisibleForTesting
+ static int halPreambleFromFrameworkPreamble(int rttManagerPreamble) {
+ BitMask checkoff = new BitMask(rttManagerPreamble);
+ int flags = 0;
+ if (checkoff.testAndClear(RttManager.PREAMBLE_LEGACY)) {
+ flags |= RttPreamble.LEGACY;
+ }
+ if (checkoff.testAndClear(RttManager.PREAMBLE_HT)) {
+ flags |= RttPreamble.HT;
+ }
+ if (checkoff.testAndClear(RttManager.PREAMBLE_VHT)) {
+ flags |= RttPreamble.VHT;
+ }
+ if (checkoff.value != 0) {
+ throw new IllegalArgumentException("bad " + rttManagerPreamble);
+ }
+ return flags;
+ }
+
+ @VisibleForTesting
+ static int frameworkPreambleFromHalPreamble(int halPreamble) {
+ BitMask checkoff = new BitMask(halPreamble);
+ int flags = 0;
+ if (checkoff.testAndClear(RttPreamble.LEGACY)) {
+ flags |= RttManager.PREAMBLE_LEGACY;
+ }
+ if (checkoff.testAndClear(RttPreamble.HT)) {
+ flags |= RttManager.PREAMBLE_HT;
+ }
+ if (checkoff.testAndClear(RttPreamble.VHT)) {
+ flags |= RttManager.PREAMBLE_VHT;
+ }
+ if (checkoff.value != 0) {
+ throw new IllegalArgumentException("bad " + halPreamble);
+ }
+ return flags;
+ }
+
+ @VisibleForTesting
+ static int halBwFromFrameworkBw(int rttManagerBandwidth) {
+ BitMask checkoff = new BitMask(rttManagerBandwidth);
+ int flags = 0;
+ if (checkoff.testAndClear(RttManager.RTT_BW_5_SUPPORT)) {
+ flags |= RttBw.BW_5MHZ;
+ }
+ if (checkoff.testAndClear(RttManager.RTT_BW_10_SUPPORT)) {
+ flags |= RttBw.BW_10MHZ;
+ }
+ if (checkoff.testAndClear(RttManager.RTT_BW_20_SUPPORT)) {
+ flags |= RttBw.BW_20MHZ;
+ }
+ if (checkoff.testAndClear(RttManager.RTT_BW_40_SUPPORT)) {
+ flags |= RttBw.BW_40MHZ;
+ }
+ if (checkoff.testAndClear(RttManager.RTT_BW_80_SUPPORT)) {
+ flags |= RttBw.BW_80MHZ;
+ }
+ if (checkoff.testAndClear(RttManager.RTT_BW_160_SUPPORT)) {
+ flags |= RttBw.BW_160MHZ;
+ }
+ if (checkoff.value != 0) {
+ throw new IllegalArgumentException("bad " + rttManagerBandwidth);
+ }
+ return flags;
+ }
+
+ @VisibleForTesting
+ static int frameworkBwFromHalBw(int rttBw) {
+ BitMask checkoff = new BitMask(rttBw);
+ int flags = 0;
+ if (checkoff.testAndClear(RttBw.BW_5MHZ)) {
+ flags |= RttManager.RTT_BW_5_SUPPORT;
+ }
+ if (checkoff.testAndClear(RttBw.BW_10MHZ)) {
+ flags |= RttManager.RTT_BW_10_SUPPORT;
+ }
+ if (checkoff.testAndClear(RttBw.BW_20MHZ)) {
+ flags |= RttManager.RTT_BW_20_SUPPORT;
+ }
+ if (checkoff.testAndClear(RttBw.BW_40MHZ)) {
+ flags |= RttManager.RTT_BW_40_SUPPORT;
+ }
+ if (checkoff.testAndClear(RttBw.BW_80MHZ)) {
+ flags |= RttManager.RTT_BW_80_SUPPORT;
+ }
+ if (checkoff.testAndClear(RttBw.BW_160MHZ)) {
+ flags |= RttManager.RTT_BW_160_SUPPORT;
+ }
+ if (checkoff.value != 0) {
+ throw new IllegalArgumentException("bad " + rttBw);
+ }
+ return flags;
+ }
+
+ @VisibleForTesting
+ static ArrayList<RttConfig> halRttConfigArrayFromFrameworkRttParamsArray(
+ RttManager.RttParams[] params) {
+ final int length = params.length;
+ ArrayList<RttConfig> configs = new ArrayList<RttConfig>(length);
+ for (int i = 0; i < length; i++) {
+ RttConfig config = halRttConfigFromFrameworkRttParams(params[i]);
+ if (config != null) {
+ configs.add(config);
+ }
+ }
+ return configs;
+ }
+
+ /**
+ * Starts a new rtt request
+ *
+ * @param params
+ * @param handler
+ * @return success indication
+ */
+ public boolean requestRtt(RttManager.RttParams[] params, WifiNative.RttEventHandler handler) {
+ ArrayList<RttConfig> rttConfigs;
+ try {
+ rttConfigs = halRttConfigArrayFromFrameworkRttParamsArray(params);
+ } catch (IllegalArgumentException e) {
+ mLog.err("Illegal argument for RTT request").c(e.toString()).flush();
+ return false;
+ }
+ synchronized (sLock) {
+ if (mIWifiRttController == null) return boolResult(false);
+ if (mRttCmdId != 0) return boolResult(false);
+ mRttCmdId = mRttCmdIdNext++;
+ mRttEventHandler = handler;
+ if (mRttCmdIdNext <= 0) mRttCmdIdNext = 1;
+ try {
+ WifiStatus status = mIWifiRttController.rangeRequest(mRttCmdId, rttConfigs);
+ if (ok(status)) return true;
+ mRttCmdId = 0;
+ return false;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Cancels an outstanding rtt request
+ *
+ * @param params
+ * @return true if there was an outstanding request and it was successfully cancelled
+ */
+ public boolean cancelRtt(RttManager.RttParams[] params) {
+ ArrayList<RttConfig> rttConfigs = halRttConfigArrayFromFrameworkRttParamsArray(params);
+ synchronized (sLock) {
+ if (mIWifiRttController == null) return boolResult(false);
+ if (mRttCmdId == 0) return boolResult(false);
+ ArrayList<byte[/* 6 */]> addrs = new ArrayList<byte[]>(rttConfigs.size());
+ for (RttConfig x : rttConfigs) addrs.add(x.addr);
+ try {
+ WifiStatus status = mIWifiRttController.rangeCancel(mRttCmdId, addrs);
+ mRttCmdId = 0;
+ if (!ok(status)) return false;
+ return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ private int mRttResponderCmdId = 0;
+
+ /**
+ * Get RTT responder information e.g. WiFi channel to enable responder on.
+ *
+ * @return info Instance of |RttResponder|, or null for error.
+ */
+ private RttResponder getRttResponder() {
+ class AnswerBox {
+ public RttResponder value = null;
+ }
+ synchronized (sLock) {
+ if (mIWifiRttController == null) return null;
+ AnswerBox answer = new AnswerBox();
+ try {
+ mIWifiRttController.getResponderInfo((status, info) -> {
+ if (!ok(status)) return;
+ answer.value = info;
+ });
+ return answer.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Convert Hal RttResponder to a framework ResponderConfig
+ *
+ * @param info Instance of |RttResponder|
+ * @return framework version of same
+ */
+ private ResponderConfig frameworkResponderConfigFromHalRttResponder(RttResponder info) {
+ ResponderConfig config = new ResponderConfig();
+ config.frequency = info.channel.centerFreq;
+ config.centerFreq0 = info.channel.centerFreq0;
+ config.centerFreq1 = info.channel.centerFreq1;
+ config.channelWidth = frameworkChannelWidthFromHalChannelWidth(info.channel.width);
+ config.preamble = frameworkPreambleFromHalPreamble(info.preamble);
+ return config;
+ }
+
+ /**
+ * Enables RTT responder role on the device.
+ *
+ * @return {@link ResponderConfig} if the responder role is successfully enabled,
+ * {@code null} otherwise.
+ */
+ public ResponderConfig enableRttResponder(int timeoutSeconds) {
+ RttResponder info = getRttResponder();
+ synchronized (sLock) {
+ if (mIWifiRttController == null) return null;
+ if (mRttResponderCmdId != 0) {
+ mLog.e("responder mode already enabled - this shouldn't happen");
+ return null;
+ }
+ ResponderConfig config = null;
+ int id = mRttCmdIdNext++;
+ if (mRttCmdIdNext <= 0) mRttCmdIdNext = 1;
+ try {
+ WifiStatus status = mIWifiRttController.enableResponder(
+ /* cmdId */id,
+ /* WifiChannelInfo channelHint */null,
+ timeoutSeconds, info);
+ if (ok(status)) {
+ mRttResponderCmdId = id;
+ config = frameworkResponderConfigFromHalRttResponder(info);
+ mVerboseLog.i("enabling rtt " + mRttResponderCmdId);
+ }
+ return config;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Disables RTT responder role.
+ *
+ * @return {@code true} if responder role is successfully disabled,
+ * {@code false} otherwise.
+ */
+ public boolean disableRttResponder() {
+ synchronized (sLock) {
+ if (mIWifiRttController == null) return boolResult(false);
+ if (mRttResponderCmdId == 0) return boolResult(false);
+ try {
+ WifiStatus status = mIWifiRttController.disableResponder(mRttResponderCmdId);
+ mRttResponderCmdId = 0;
+ if (!ok(status)) return false;
+ return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set the MAC OUI during scanning.
+ * <p>
+ * An OUI {Organizationally Unique Identifier} is a 24-bit number that
+ * uniquely identifies a vendor or manufacturer.
+ *
+ * @param oui
+ * @return true for success
+ */
+ public boolean setScanningMacOui(byte[] oui) {
+ if (oui == null) return boolResult(false);
+ if (oui.length != 3) return boolResult(false);
+ synchronized (sLock) {
+ try {
+ if (mIWifiStaIface == null) return boolResult(false);
+ WifiStatus status = mIWifiStaIface.setScanningMacOui(oui);
+ if (!ok(status)) return false;
+ return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Query the list of valid frequencies for the provided band.
+ * <p>
+ * The result depends on the on the country code that has been set.
+ *
+ * @param band as specified by one of the WifiScanner.WIFI_BAND_* constants.
+ * @return frequencies vector of valid frequencies (MHz), or null for error.
+ * @throws IllegalArgumentException if band is not recognized.
+ */
+ public int[] getChannelsForBand(int band) {
+ enter("%").c(band).flush();
+ class AnswerBox {
+ public int[] value = null;
+ }
+ synchronized (sLock) {
+ try {
+ AnswerBox box = new AnswerBox();
+ int hb = makeWifiBandFromFrameworkBand(band);
+ if (mIWifiStaIface != null) {
+ mIWifiStaIface.getValidFrequenciesForBand(hb, (status, frequencies) -> {
+ if (status.code == WifiStatusCode.ERROR_NOT_SUPPORTED) {
+ mChannelsForBandSupport = false;
+ }
+ if (!ok(status)) return;
+ mChannelsForBandSupport = true;
+ box.value = intArrayFromArrayList(frequencies);
+ });
+ } else if (mIWifiApIface != null) {
+ mIWifiApIface.getValidFrequenciesForBand(hb, (status, frequencies) -> {
+ if (status.code == WifiStatusCode.ERROR_NOT_SUPPORTED) {
+ mChannelsForBandSupport = false;
+ }
+ if (!ok(status)) return;
+ mChannelsForBandSupport = true;
+ box.value = intArrayFromArrayList(frequencies);
+ });
+ }
+ return box.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return null;
+ }
+ }
+ }
+
+ private int[] intArrayFromArrayList(ArrayList<Integer> in) {
+ int[] ans = new int[in.size()];
+ int i = 0;
+ for (Integer e : in) ans[i++] = e;
+ return ans;
+ }
+
+ /**
+ * This holder is null until we know whether or not there is frequency-for-band support.
+ * <p>
+ * Set as a side-effect of getChannelsForBand.
+ */
+ @VisibleForTesting
+ Boolean mChannelsForBandSupport = null;
+
+ /**
+ * Indicates whether getChannelsForBand is supported.
+ *
+ * @return true if it is.
+ */
+ public boolean isGetChannelsForBandSupported() {
+ if (mChannelsForBandSupport != null) return mChannelsForBandSupport;
+ getChannelsForBand(WifiBand.BAND_24GHZ);
+ if (mChannelsForBandSupport != null) return mChannelsForBandSupport;
+ return false;
+ }
+
+ /**
+ * Get the APF (Android Packet Filter) capabilities of the device
+ */
+ public ApfCapabilities getApfCapabilities() {
+ class AnswerBox {
+ public ApfCapabilities value = sNoApfCapabilities;
+ }
+ synchronized (sLock) {
+ try {
+ if (mIWifiStaIface == null) return sNoApfCapabilities;
+ AnswerBox box = new AnswerBox();
+ mIWifiStaIface.getApfPacketFilterCapabilities((status, capabilities) -> {
+ if (!ok(status)) return;
+ box.value = new ApfCapabilities(
+ /* apfVersionSupported */ capabilities.version,
+ /* maximumApfProgramSize */ capabilities.maxLength,
+ /* apfPacketFormat */ android.system.OsConstants.ARPHRD_ETHER);
+ });
+ return box.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return sNoApfCapabilities;
+ }
+ }
+ }
+
+ private static final ApfCapabilities sNoApfCapabilities = new ApfCapabilities(0, 0, 0);
+
+ /**
+ * Installs an APF program on this iface, replacing any existing program.
+ *
+ * @param filter is the android packet filter program
+ * @return true for success
+ */
+ public boolean installPacketFilter(byte[] filter) {
+ int cmdId = 0; // We only aspire to support one program at a time
+ if (filter == null) return boolResult(false);
+ // Copy the program before taking the lock.
+ ArrayList<Byte> program = NativeUtil.byteArrayToArrayList(filter);
+ enter("filter length %").c(filter.length).flush();
+ synchronized (sLock) {
+ try {
+ if (mIWifiStaIface == null) return boolResult(false);
+ WifiStatus status = mIWifiStaIface.installApfPacketFilter(cmdId, program);
+ if (!ok(status)) return false;
+ return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Set country code for this AP iface.
+ *
+ * @param countryCode - two-letter country code (as ISO 3166)
+ * @return true for success
+ */
+ public boolean setCountryCodeHal(String countryCode) {
+ if (countryCode == null) return boolResult(false);
+ if (countryCode.length() != 2) return boolResult(false);
+ byte[] code;
+ try {
+ code = NativeUtil.stringToByteArray(countryCode);
+ } catch (IllegalArgumentException e) {
+ return boolResult(false);
+ }
+ synchronized (sLock) {
+ try {
+ if (mIWifiApIface == null) return boolResult(false);
+ WifiStatus status = mIWifiApIface.setCountryCode(code);
+ if (!ok(status)) return false;
+ return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ private WifiNative.WifiLoggerEventHandler mLogEventHandler = null;
+
+ /**
+ * Registers the logger callback and enables alerts.
+ * Ring buffer data collection is only triggered when |startLoggingRingBuffer| is invoked.
+ */
+ public boolean setLoggingEventHandler(WifiNative.WifiLoggerEventHandler handler) {
+ if (handler == null) return boolResult(false);
+ synchronized (sLock) {
+ if (mIWifiChip == null) return boolResult(false);
+ if (mLogEventHandler != null) return boolResult(false);
+ try {
+ WifiStatus status = mIWifiChip.enableDebugErrorAlerts(true);
+ if (!ok(status)) return false;
+ mLogEventHandler = handler;
+ return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Stops all logging and resets the logger callback.
+ * This stops both the alerts and ring buffer data collection.
+ */
+ public boolean resetLogHandler() {
+ synchronized (sLock) {
+ if (mIWifiChip == null) return boolResult(false);
+ if (mLogEventHandler == null) return boolResult(false);
+ try {
+ WifiStatus status = mIWifiChip.enableDebugErrorAlerts(false);
+ if (!ok(status)) return false;
+ status = mIWifiChip.stopLoggingToDebugRingBuffer();
+ if (!ok(status)) return false;
+ mLogEventHandler = null;
+ return true;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Control debug data collection
+ *
+ * @param verboseLevel 0 to 3, inclusive. 0 stops logging.
+ * @param flags Ignored.
+ * @param maxIntervalInSec Maximum interval between reports; ignore if 0.
+ * @param minDataSizeInBytes Minimum data size in buffer for report; ignore if 0.
+ * @param ringName Name of the ring for which data collection is to start.
+ * @return true for success
+ */
+ public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxIntervalInSec,
+ int minDataSizeInBytes, String ringName) {
+ enter("verboseLevel=%, flags=%, maxIntervalInSec=%, minDataSizeInBytes=%, ringName=%")
+ .c(verboseLevel).c(flags).c(maxIntervalInSec).c(minDataSizeInBytes).c(ringName)
+ .flush();
+ synchronized (sLock) {
+ if (mIWifiChip == null) return boolResult(false);
+ try {
+ // note - flags are not used
+ WifiStatus status = mIWifiChip.startLoggingToDebugRingBuffer(
+ ringName,
+ verboseLevel,
+ maxIntervalInSec,
+ minDataSizeInBytes
+ );
+ return ok(status);
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Pointlessly fail
+ *
+ * @return -1
+ */
+ public int getSupportedLoggerFeatureSet() {
+ return -1;
+ }
+
+ private String mDriverDescription; // Cached value filled by requestChipDebugInfo()
+
+ /**
+ * Vendor-provided wifi driver version string
+ */
+ public String getDriverVersion() {
+ synchronized (sLock) {
+ if (mDriverDescription == null) requestChipDebugInfo();
+ return mDriverDescription;
+ }
+ }
+
+ private String mFirmwareDescription; // Cached value filled by requestChipDebugInfo()
+
+ /**
+ * Vendor-provided wifi firmware version string
+ */
+ public String getFirmwareVersion() {
+ synchronized (sLock) {
+ if (mFirmwareDescription == null) requestChipDebugInfo();
+ return mFirmwareDescription;
+ }
+ }
+
+ /**
+ * Refreshes our idea of the driver and firmware versions
+ */
+ private void requestChipDebugInfo() {
+ mDriverDescription = null;
+ mFirmwareDescription = null;
+ try {
+ if (mIWifiChip == null) return;
+ mIWifiChip.requestChipDebugInfo((status, chipDebugInfo) -> {
+ if (!ok(status)) return;
+ mDriverDescription = chipDebugInfo.driverDescription;
+ mFirmwareDescription = chipDebugInfo.firmwareDescription;
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return;
+ }
+ mLog.info("Driver: % Firmware: %")
+ .c(mDriverDescription)
+ .c(mFirmwareDescription)
+ .flush();
+ }
+
+ /**
+ * Creates RingBufferStatus from the Hal version
+ */
+ private static WifiNative.RingBufferStatus ringBufferStatus(WifiDebugRingBufferStatus h) {
+ WifiNative.RingBufferStatus ans = new WifiNative.RingBufferStatus();
+ ans.name = h.ringName;
+ ans.flag = frameworkRingBufferFlagsFromHal(h.flags);
+ ans.ringBufferId = h.ringId;
+ ans.ringBufferByteSize = h.sizeInBytes;
+ ans.verboseLevel = h.verboseLevel;
+ // Remaining fields are unavailable
+ // writtenBytes;
+ // readBytes;
+ // writtenRecords;
+ return ans;
+ }
+
+ /**
+ * Translates a hal wifiDebugRingBufferFlag to the WifiNative version
+ */
+ private static int frameworkRingBufferFlagsFromHal(int wifiDebugRingBufferFlag) {
+ BitMask checkoff = new BitMask(wifiDebugRingBufferFlag);
+ int flags = 0;
+ if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_BINARY_ENTRIES)) {
+ flags |= WifiNative.RingBufferStatus.HAS_BINARY_ENTRIES;
+ }
+ if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_ASCII_ENTRIES)) {
+ flags |= WifiNative.RingBufferStatus.HAS_ASCII_ENTRIES;
+ }
+ if (checkoff.testAndClear(WifiDebugRingBufferFlags.HAS_PER_PACKET_ENTRIES)) {
+ flags |= WifiNative.RingBufferStatus.HAS_PER_PACKET_ENTRIES;
+ }
+ if (checkoff.value != 0) {
+ throw new IllegalArgumentException("Unknown WifiDebugRingBufferFlag " + checkoff.value);
+ }
+ return flags;
+ }
+
+ /**
+ * Creates array of RingBufferStatus from the Hal version
+ */
+ private static WifiNative.RingBufferStatus[] makeRingBufferStatusArray(
+ ArrayList<WifiDebugRingBufferStatus> ringBuffers) {
+ WifiNative.RingBufferStatus[] ans = new WifiNative.RingBufferStatus[ringBuffers.size()];
+ int i = 0;
+ for (WifiDebugRingBufferStatus b : ringBuffers) {
+ ans[i++] = ringBufferStatus(b);
+ }
+ return ans;
+ }
+
+ /**
+ * API to get the status of all ring buffers supported by driver
+ */
+ public WifiNative.RingBufferStatus[] getRingBufferStatus() {
+ class AnswerBox {
+ public WifiNative.RingBufferStatus[] value = null;
+ }
+ AnswerBox ans = new AnswerBox();
+ synchronized (sLock) {
+ if (mIWifiChip == null) return null;
+ try {
+ mIWifiChip.getDebugRingBuffersStatus((status, ringBuffers) -> {
+ if (!ok(status)) return;
+ ans.value = makeRingBufferStatusArray(ringBuffers);
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return null;
+ }
+ }
+ return ans.value;
+ }
+
+ /**
+ * Indicates to driver that all the data has to be uploaded urgently
+ */
+ public boolean getRingBufferData(String ringName) {
+ enter("ringName %").c(ringName).flush();
+ synchronized (sLock) {
+ if (mIWifiChip == null) return boolResult(false);
+ try {
+ WifiStatus status = mIWifiChip.forceDumpToDebugRingBuffer(ringName);
+ return ok(status);
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Request vendor debug info from the firmware
+ */
+ public byte[] getFwMemoryDump() {
+ class AnswerBox {
+ public byte[] value;
+ }
+ AnswerBox ans = new AnswerBox();
+ synchronized (sLock) {
+ if (mIWifiChip == null) return (null);
+ try {
+ mIWifiChip.requestFirmwareDebugDump((status, blob) -> {
+ if (!ok(status)) return;
+ ans.value = NativeUtil.byteArrayFromArrayList(blob);
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return null;
+ }
+ }
+ return ans.value;
+ }
+
+ /**
+ * Request vendor debug info from the driver
+ */
+ public byte[] getDriverStateDump() {
+ class AnswerBox {
+ public byte[] value;
+ }
+ AnswerBox ans = new AnswerBox();
+ synchronized (sLock) {
+ if (mIWifiChip == null) return (null);
+ try {
+ mIWifiChip.requestDriverDebugDump((status, blob) -> {
+ if (!ok(status)) return;
+ ans.value = NativeUtil.byteArrayFromArrayList(blob);
+ });
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return null;
+ }
+ }
+ return ans.value;
+ }
+
+ /**
+ * Start packet fate monitoring
+ * <p>
+ * Once started, monitoring remains active until HAL is unloaded.
+ *
+ * @return true for success
+ */
+ public boolean startPktFateMonitoring() {
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return boolResult(false);
+ try {
+ WifiStatus status = mIWifiStaIface.startDebugPacketFateMonitoring();
+ return ok(status);
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ private byte halToFrameworkPktFateFrameType(int type) {
+ switch (type) {
+ case WifiDebugPacketFateFrameType.UNKNOWN:
+ return WifiLoggerHal.FRAME_TYPE_UNKNOWN;
+ case WifiDebugPacketFateFrameType.ETHERNET_II:
+ return WifiLoggerHal.FRAME_TYPE_ETHERNET_II;
+ case WifiDebugPacketFateFrameType.MGMT_80211:
+ return WifiLoggerHal.FRAME_TYPE_80211_MGMT;
+ default:
+ throw new IllegalArgumentException("bad " + type);
+ }
+ }
+
+ private byte halToFrameworkRxPktFate(int type) {
+ switch (type) {
+ case WifiDebugRxPacketFate.SUCCESS:
+ return WifiLoggerHal.RX_PKT_FATE_SUCCESS;
+ case WifiDebugRxPacketFate.FW_QUEUED:
+ return WifiLoggerHal.RX_PKT_FATE_FW_QUEUED;
+ case WifiDebugRxPacketFate.FW_DROP_FILTER:
+ return WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER;
+ case WifiDebugRxPacketFate.FW_DROP_INVALID:
+ return WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID;
+ case WifiDebugRxPacketFate.FW_DROP_NOBUFS:
+ return WifiLoggerHal.RX_PKT_FATE_FW_DROP_NOBUFS;
+ case WifiDebugRxPacketFate.FW_DROP_OTHER:
+ return WifiLoggerHal.RX_PKT_FATE_FW_DROP_OTHER;
+ case WifiDebugRxPacketFate.DRV_QUEUED:
+ return WifiLoggerHal.RX_PKT_FATE_DRV_QUEUED;
+ case WifiDebugRxPacketFate.DRV_DROP_FILTER:
+ return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_FILTER;
+ case WifiDebugRxPacketFate.DRV_DROP_INVALID:
+ return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_INVALID;
+ case WifiDebugRxPacketFate.DRV_DROP_NOBUFS:
+ return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_NOBUFS;
+ case WifiDebugRxPacketFate.DRV_DROP_OTHER:
+ return WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER;
+ default:
+ throw new IllegalArgumentException("bad " + type);
+ }
+ }
+
+ private byte halToFrameworkTxPktFate(int type) {
+ switch (type) {
+ case WifiDebugTxPacketFate.ACKED:
+ return WifiLoggerHal.TX_PKT_FATE_ACKED;
+ case WifiDebugTxPacketFate.SENT:
+ return WifiLoggerHal.TX_PKT_FATE_SENT;
+ case WifiDebugTxPacketFate.FW_QUEUED:
+ return WifiLoggerHal.TX_PKT_FATE_FW_QUEUED;
+ case WifiDebugTxPacketFate.FW_DROP_INVALID:
+ return WifiLoggerHal.TX_PKT_FATE_FW_DROP_INVALID;
+ case WifiDebugTxPacketFate.FW_DROP_NOBUFS:
+ return WifiLoggerHal.TX_PKT_FATE_FW_DROP_NOBUFS;
+ case WifiDebugTxPacketFate.FW_DROP_OTHER:
+ return WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER;
+ case WifiDebugTxPacketFate.DRV_QUEUED:
+ return WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED;
+ case WifiDebugTxPacketFate.DRV_DROP_INVALID:
+ return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_INVALID;
+ case WifiDebugTxPacketFate.DRV_DROP_NOBUFS:
+ return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_NOBUFS;
+ case WifiDebugTxPacketFate.DRV_DROP_OTHER:
+ return WifiLoggerHal.TX_PKT_FATE_DRV_DROP_OTHER;
+ default:
+ throw new IllegalArgumentException("bad " + type);
+ }
+ }
+
+ /**
+ * Retrieve fates of outbound packets
+ * <p>
+ * Reports the outbound frames for the most recent association (space allowing).
+ *
+ * @param reportBufs
+ * @return true for success
+ */
+ public boolean getTxPktFates(WifiNative.TxFateReport[] reportBufs) {
+ if (ArrayUtils.isEmpty(reportBufs)) return boolResult(false);
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return boolResult(false);
+ try {
+ MutableBoolean ok = new MutableBoolean(false);
+ mIWifiStaIface.getDebugTxPacketFates((status, fates) -> {
+ if (!ok(status)) return;
+ int i = 0;
+ for (WifiDebugTxPacketFateReport fate : fates) {
+ if (i >= reportBufs.length) break;
+ byte code = halToFrameworkTxPktFate(fate.fate);
+ long us = fate.frameInfo.driverTimestampUsec;
+ byte type =
+ halToFrameworkPktFateFrameType(fate.frameInfo.frameType);
+ byte[] frame =
+ NativeUtil.byteArrayFromArrayList(
+ fate.frameInfo.frameContent);
+ reportBufs[i++] =
+ new WifiNative.TxFateReport(code, us, type, frame);
+ }
+ ok.value = true;
+ }
+ );
+ return ok.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Retrieve fates of inbound packets
+ * <p>
+ * Reports the inbound frames for the most recent association (space allowing).
+ *
+ * @param reportBufs
+ * @return true for success
+ */
+ public boolean getRxPktFates(WifiNative.RxFateReport[] reportBufs) {
+ if (ArrayUtils.isEmpty(reportBufs)) return boolResult(false);
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return boolResult(false);
+ try {
+ MutableBoolean ok = new MutableBoolean(false);
+ mIWifiStaIface.getDebugRxPacketFates((status, fates) -> {
+ if (!ok(status)) return;
+ int i = 0;
+ for (WifiDebugRxPacketFateReport fate : fates) {
+ if (i >= reportBufs.length) break;
+ byte code = halToFrameworkRxPktFate(fate.fate);
+ long us = fate.frameInfo.driverTimestampUsec;
+ byte type =
+ halToFrameworkPktFateFrameType(fate.frameInfo.frameType);
+ byte[] frame =
+ NativeUtil.byteArrayFromArrayList(
+ fate.frameInfo.frameContent);
+ reportBufs[i++] =
+ new WifiNative.RxFateReport(code, us, type, frame);
+ }
+ ok.value = true;
+ }
+ );
+ return ok.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Start sending the specified keep alive packets periodically.
+ *
+ * @param slot
+ * @param srcMac
+ * @param keepAlivePacket
+ * @param periodInMs
+ * @return 0 for success, -1 for error
+ */
+ public int startSendingOffloadedPacket(
+ int slot, byte[] srcMac, KeepalivePacketData keepAlivePacket, int periodInMs) {
+ enter("slot=% periodInMs=%").c(slot).c(periodInMs).flush();
+
+ ArrayList<Byte> data = NativeUtil.byteArrayToArrayList(keepAlivePacket.data);
+ short protocol = (short) (keepAlivePacket.protocol);
+
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return -1;
+ try {
+ WifiStatus status = mIWifiStaIface.startSendingKeepAlivePackets(
+ slot,
+ data,
+ protocol,
+ srcMac,
+ keepAlivePacket.dstMac,
+ periodInMs);
+ if (!ok(status)) return -1;
+ return 0;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * Stop sending the specified keep alive packets.
+ *
+ * @param slot id - same as startSendingOffloadedPacket call.
+ * @return 0 for success, -1 for error
+ */
+ public int stopSendingOffloadedPacket(int slot) {
+ enter("slot=%").c(slot).flush();
+
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return -1;
+ try {
+ WifiStatus status = mIWifiStaIface.stopSendingKeepAlivePackets(slot);
+ if (!ok(status)) return -1;
+ return 0;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * A fixed cmdId for our RssiMonitoring (we only do one at a time)
+ */
+ @VisibleForTesting
+ static final int sRssiMonCmdId = 7551;
+
+ /**
+ * Our client's handler
+ */
+ private WifiNative.WifiRssiEventHandler mWifiRssiEventHandler;
+
+ /**
+ * Start RSSI monitoring on the currently connected access point.
+ *
+ * @param maxRssi Maximum RSSI threshold.
+ * @param minRssi Minimum RSSI threshold.
+ * @param rssiEventHandler Called when RSSI goes above maxRssi or below minRssi
+ * @return 0 for success, -1 for failure
+ */
+ public int startRssiMonitoring(byte maxRssi, byte minRssi,
+ WifiNative.WifiRssiEventHandler rssiEventHandler) {
+ enter("maxRssi=% minRssi=%").c(maxRssi).c(minRssi).flush();
+ if (maxRssi <= minRssi) return -1;
+ if (rssiEventHandler == null) return -1;
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return -1;
+ try {
+ mIWifiStaIface.stopRssiMonitoring(sRssiMonCmdId);
+ WifiStatus status;
+ status = mIWifiStaIface.startRssiMonitoring(sRssiMonCmdId, maxRssi, minRssi);
+ if (!ok(status)) return -1;
+ mWifiRssiEventHandler = rssiEventHandler;
+ return 0;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * Stop RSSI monitoring
+ *
+ * @return 0 for success, -1 for failure
+ */
+ public int stopRssiMonitoring() {
+ synchronized (sLock) {
+ mWifiRssiEventHandler = null;
+ if (mIWifiStaIface == null) return -1;
+ try {
+ mIWifiStaIface.stopRssiMonitoring(sRssiMonCmdId);
+ WifiStatus status = mIWifiStaIface.stopRssiMonitoring(sRssiMonCmdId);
+ if (!ok(status)) return -1;
+ return 0;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return -1;
+ }
+ }
+ }
+
+ //TODO - belongs in NativeUtil
+ private static int[] intsFromArrayList(ArrayList<Integer> a) {
+ if (a == null) return null;
+ int[] b = new int[a.size()];
+ int i = 0;
+ for (Integer e : a) b[i++] = e;
+ return b;
+ }
+
+ /**
+ * Translates from Hal version of wake reason stats to the framework version of same
+ *
+ * @param h - Hal version of wake reason stats
+ * @return framework version of same
+ */
+ private static WifiWakeReasonAndCounts halToFrameworkWakeReasons(
+ WifiDebugHostWakeReasonStats h) {
+ if (h == null) return null;
+ WifiWakeReasonAndCounts ans = new WifiWakeReasonAndCounts();
+ ans.totalCmdEventWake = h.totalCmdEventWakeCnt;
+ ans.totalDriverFwLocalWake = h.totalDriverFwLocalWakeCnt;
+ ans.totalRxDataWake = h.totalRxPacketWakeCnt;
+ ans.rxUnicast = h.rxPktWakeDetails.rxUnicastCnt;
+ ans.rxMulticast = h.rxPktWakeDetails.rxMulticastCnt;
+ ans.rxBroadcast = h.rxPktWakeDetails.rxBroadcastCnt;
+ ans.icmp = h.rxIcmpPkWakeDetails.icmpPkt;
+ ans.icmp6 = h.rxIcmpPkWakeDetails.icmp6Pkt;
+ ans.icmp6Ra = h.rxIcmpPkWakeDetails.icmp6Ra;
+ ans.icmp6Na = h.rxIcmpPkWakeDetails.icmp6Na;
+ ans.icmp6Ns = h.rxIcmpPkWakeDetails.icmp6Ns;
+ ans.ipv4RxMulticast = h.rxMulticastPkWakeDetails.ipv4RxMulticastAddrCnt;
+ ans.ipv6Multicast = h.rxMulticastPkWakeDetails.ipv6RxMulticastAddrCnt;
+ ans.otherRxMulticast = h.rxMulticastPkWakeDetails.otherRxMulticastAddrCnt;
+ ans.cmdEventWakeCntArray = intsFromArrayList(h.cmdEventWakeCntPerType);
+ ans.driverFWLocalWakeCntArray = intsFromArrayList(h.driverFwLocalWakeCntPerType);
+ return ans;
+ }
+
+ /**
+ * Fetch the host wakeup reasons stats from wlan driver.
+ *
+ * @return the |WifiWakeReasonAndCounts| from the wlan driver, or null on failure.
+ */
+ public WifiWakeReasonAndCounts getWlanWakeReasonCount() {
+ class AnswerBox {
+ public WifiDebugHostWakeReasonStats value = null;
+ }
+ AnswerBox ans = new AnswerBox();
+ synchronized (sLock) {
+ if (mIWifiChip == null) return null;
+ try {
+ mIWifiChip.getDebugHostWakeReasonStats((status, stats) -> {
+ if (ok(status)) {
+ ans.value = stats;
+ }
+ });
+ return halToFrameworkWakeReasons(ans.value);
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Enable/Disable Neighbour discovery offload functionality in the firmware.
+ *
+ * @param enabled true to enable, false to disable.
+ */
+ public boolean configureNeighborDiscoveryOffload(boolean enabled) {
+ enter("enabled=%").c(enabled).flush();
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return boolResult(false);
+ try {
+ WifiStatus status = mIWifiStaIface.enableNdOffload(enabled);
+ if (!ok(status)) return false;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Firmware roaming control.
+
+ /**
+ * Query the firmware roaming capabilities.
+ *
+ * @param capabilities object to be filled in
+ * @return true for success; false for failure
+ */
+ public boolean getRoamingCapabilities(WifiNative.RoamingCapabilities capabilities) {
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return boolResult(false);
+ try {
+ MutableBoolean ok = new MutableBoolean(false);
+ WifiNative.RoamingCapabilities out = capabilities;
+ mIWifiStaIface.getRoamingCapabilities((status, cap) -> {
+ if (!ok(status)) return;
+ out.maxBlacklistSize = cap.maxBlacklistSize;
+ out.maxWhitelistSize = cap.maxWhitelistSize;
+ ok.value = true;
+ });
+ return ok.value;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Enable/disable firmware roaming.
+ *
+ * @param state the intended roaming state
+ * @return SUCCESS, FAILURE, or BUSY
+ */
+ public int enableFirmwareRoaming(int state) {
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return WifiStatusCode.ERROR_NOT_STARTED;
+ try {
+ byte val;
+ switch (state) {
+ case WifiNative.DISABLE_FIRMWARE_ROAMING:
+ val = StaRoamingState.DISABLED;
+ break;
+ case WifiNative.ENABLE_FIRMWARE_ROAMING:
+ val = StaRoamingState.ENABLED;
+ break;
+ default:
+ mLog.e("enableFirmwareRoaming invalid argument " + state);
+ return WifiStatusCode.ERROR_INVALID_ARGS;
+ }
+
+ WifiStatus status = mIWifiStaIface.setRoamingState(val);
+ mVerboseLog.d("setRoamingState returned " + status.code);
+ return status.code;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return WifiStatusCode.ERROR_UNKNOWN;
+ }
+ }
+ }
+
+ /**
+ * Set firmware roaming configurations.
+ *
+ * @param config new roaming configuration object
+ * @return true for success; false for failure
+ */
+ public boolean configureRoaming(WifiNative.RoamingConfig config) {
+ synchronized (sLock) {
+ if (mIWifiStaIface == null) return boolResult(false);
+ try {
+ StaRoamingConfig roamingConfig = new StaRoamingConfig();
+
+ // parse the blacklist BSSIDs if any
+ if (config.blacklistBssids != null) {
+ for (String bssid : config.blacklistBssids) {
+ byte[] mac = NativeUtil.macAddressToByteArray(bssid);
+ roamingConfig.bssidBlacklist.add(mac);
+ }
+ }
+
+ // parse the whitelist SSIDs if any
+ if (config.whitelistSsids != null) {
+ for (String ssidStr : config.whitelistSsids) {
+ String unquotedSsidStr = WifiInfo.removeDoubleQuotes(ssidStr);
+
+ int len = unquotedSsidStr.length();
+ if (len > 32) {
+ mLog.err("configureRoaming: skip invalid SSID %")
+ .r(unquotedSsidStr).flush();
+ continue;
+ }
+ byte[] ssid = new byte[len];
+ for (int i = 0; i < len; i++) {
+ ssid[i] = (byte) unquotedSsidStr.charAt(i);
+ }
+ roamingConfig.ssidWhitelist.add(ssid);
+ }
+ }
+
+ WifiStatus status = mIWifiStaIface.configureRoaming(roamingConfig);
+ if (!ok(status)) return false;
+ } catch (RemoteException e) {
+ handleRemoteException(e);
+ return false;
+ } catch (IllegalArgumentException e) {
+ mLog.err("Illegal argument for roaming configuration").c(e.toString()).flush();
+ return false;
+ }
+ return true;
+ }
+ }
+
+ // This creates a blob of IE elements from the array received.
+ // TODO: This ugly conversion can be removed if we put IE elements in ScanResult.
+ private static byte[] hidlIeArrayToFrameworkIeBlob(ArrayList<WifiInformationElement> ies) {
+ if (ies == null || ies.isEmpty()) return new byte[0];
+ ArrayList<Byte> ieBlob = new ArrayList<>();
+ for (WifiInformationElement ie : ies) {
+ ieBlob.add(ie.id);
+ ieBlob.addAll(ie.data);
+ }
+ return NativeUtil.byteArrayFromArrayList(ieBlob);
+ }
+
+ // This is only filling up the fields of Scan Result used by Gscan clients.
+ private static ScanResult hidlToFrameworkScanResult(StaScanResult scanResult) {
+ if (scanResult == null) return null;
+ ScanResult frameworkScanResult = new ScanResult();
+ frameworkScanResult.SSID = NativeUtil.encodeSsid(scanResult.ssid);
+ frameworkScanResult.wifiSsid =
+ WifiSsid.createFromByteArray(NativeUtil.byteArrayFromArrayList(scanResult.ssid));
+ frameworkScanResult.BSSID = NativeUtil.macAddressFromByteArray(scanResult.bssid);
+ frameworkScanResult.level = scanResult.rssi;
+ frameworkScanResult.frequency = scanResult.frequency;
+ frameworkScanResult.timestamp = scanResult.timeStampInUs;
+ frameworkScanResult.bytes = hidlIeArrayToFrameworkIeBlob(scanResult.informationElements);
+ return frameworkScanResult;
+ }
+
+ private static ScanResult[] hidlToFrameworkScanResults(ArrayList<StaScanResult> scanResults) {
+ if (scanResults == null || scanResults.isEmpty()) return new ScanResult[0];
+ ScanResult[] frameworkScanResults = new ScanResult[scanResults.size()];
+ int i = 0;
+ for (StaScanResult scanResult : scanResults) {
+ frameworkScanResults[i++] = hidlToFrameworkScanResult(scanResult);
+ }
+ return frameworkScanResults;
+ }
+
+ /**
+ * This just returns whether the scan was interrupted or not.
+ */
+ private static int hidlToFrameworkScanDataFlags(int flag) {
+ if (flag == StaScanDataFlagMask.INTERRUPTED) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ private static WifiScanner.ScanData[] hidlToFrameworkScanDatas(
+ int cmdId, ArrayList<StaScanData> scanDatas) {
+ if (scanDatas == null || scanDatas.isEmpty()) return new WifiScanner.ScanData[0];
+ WifiScanner.ScanData[] frameworkScanDatas = new WifiScanner.ScanData[scanDatas.size()];
+ int i = 0;
+ for (StaScanData scanData : scanDatas) {
+ int flags = hidlToFrameworkScanDataFlags(scanData.flags);
+ ScanResult[] frameworkScanResults = hidlToFrameworkScanResults(scanData.results);
+ frameworkScanDatas[i++] =
+ new WifiScanner.ScanData(cmdId, flags, scanData.bucketsScanned, false,
+ frameworkScanResults);
+ }
+ return frameworkScanDatas;
+ }
+
+ /**
+ * Callback for events on the STA interface.
+ */
+ private class StaIfaceEventCallback extends IWifiStaIfaceEventCallback.Stub {
+ @Override
+ public void onBackgroundScanFailure(int cmdId) {
+ mVerboseLog.d("onBackgroundScanFailure " + cmdId);
+ WifiNative.ScanEventHandler eventHandler;
+ synchronized (sLock) {
+ if (mScan == null || cmdId != mScan.cmdId) return;
+ eventHandler = mScan.eventHandler;
+ }
+ eventHandler.onScanStatus(WifiNative.WIFI_SCAN_FAILED);
+ }
+
+ @Override
+ public void onBackgroundFullScanResult(
+ int cmdId, int bucketsScanned, StaScanResult result) {
+ mVerboseLog.d("onBackgroundFullScanResult " + cmdId);
+ WifiNative.ScanEventHandler eventHandler;
+ synchronized (sLock) {
+ if (mScan == null || cmdId != mScan.cmdId) return;
+ eventHandler = mScan.eventHandler;
+ }
+ eventHandler.onFullScanResult(hidlToFrameworkScanResult(result), bucketsScanned);
+ }
+
+ @Override
+ public void onBackgroundScanResults(int cmdId, ArrayList<StaScanData> scanDatas) {
+ mVerboseLog.d("onBackgroundScanResults " + cmdId);
+ WifiNative.ScanEventHandler eventHandler;
+ // WifiScanner currently uses the results callback to fetch the scan results.
+ // So, simulate that by sending out the notification and then caching the results
+ // locally. This will then be returned to WifiScanner via getScanResults.
+ synchronized (sLock) {
+ if (mScan == null || cmdId != mScan.cmdId) return;
+ eventHandler = mScan.eventHandler;
+ mScan.latestScanResults = hidlToFrameworkScanDatas(cmdId, scanDatas);
+ }
+ eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
+ }
+
+ @Override
+ public void onRssiThresholdBreached(int cmdId, byte[/* 6 */] currBssid, int currRssi) {
+ mVerboseLog.d("onRssiThresholdBreached " + cmdId + "currRssi " + currRssi);
+ WifiNative.WifiRssiEventHandler eventHandler;
+ synchronized (sLock) {
+ if (mWifiRssiEventHandler == null || cmdId != sRssiMonCmdId) return;
+ eventHandler = mWifiRssiEventHandler;
+ }
+ eventHandler.onRssiThresholdBreached((byte) currRssi);
+ }
+ }
+
+ /**
+ * Callback for events on the STA interface.
+ */
+ private class ChipEventCallback extends IWifiChipEventCallback.Stub {
+ @Override
+ public void onChipReconfigured(int modeId) {
+ mVerboseLog.d("onChipReconfigured " + modeId);
+ }
+
+ @Override
+ public void onChipReconfigureFailure(WifiStatus status) {
+ mVerboseLog.d("onChipReconfigureFailure " + status);
+ }
+
+ public void onIfaceAdded(int type, String name) {
+ mVerboseLog.d("onIfaceAdded " + type + ", name: " + name);
+ }
+
+ @Override
+ public void onIfaceRemoved(int type, String name) {
+ mVerboseLog.d("onIfaceRemoved " + type + ", name: " + name);
+ }
+
+ @Override
+ public void onDebugRingBufferDataAvailable(
+ WifiDebugRingBufferStatus status, java.util.ArrayList<Byte> data) {
+ //TODO(b/35875078) Reinstate logging when execessive callbacks are fixed
+ // mVerboseLog.d("onDebugRingBufferDataAvailable " + status);
+ mHalEventHandler.post(() -> {
+ WifiNative.WifiLoggerEventHandler eventHandler;
+ synchronized (sLock) {
+ if (mLogEventHandler == null || status == null || data == null) return;
+ eventHandler = mLogEventHandler;
+ }
+ // Because |sLock| has been released, there is a chance that we'll execute
+ // a spurious callback (after someone has called resetLogHandler()).
+ //
+ // However, the alternative risks deadlock. Consider:
+ // [T1.1] WifiDiagnostics.captureBugReport()
+ // [T1.2] -- acquire WifiDiagnostics object's intrinsic lock
+ // [T1.3] -> WifiVendorHal.getRingBufferData()
+ // [T1.4] -- acquire WifiVendorHal.sLock
+ // [T2.1] <lambda>()
+ // [T2.2] -- acquire WifiVendorHal.sLock
+ // [T2.3] -> WifiDiagnostics.onRingBufferData()
+ // [T2.4] -- acquire WifiDiagnostics object's intrinsic lock
+ //
+ // The problem here is that the two threads acquire the locks in opposite order.
+ // If, for example, T2.2 executes between T1.2 and 1.4, then T1 and T2
+ // will be deadlocked.
+ eventHandler.onRingBufferData(
+ ringBufferStatus(status), NativeUtil.byteArrayFromArrayList(data));
+ });
+ }
+
+ @Override
+ public void onDebugErrorAlert(int errorCode, java.util.ArrayList<Byte> debugData) {
+ mVerboseLog.d("onDebugErrorAlert " + errorCode);
+ mHalEventHandler.post(() -> {
+ WifiNative.WifiLoggerEventHandler eventHandler;
+ synchronized (sLock) {
+ if (mLogEventHandler == null || debugData == null) return;
+ eventHandler = mLogEventHandler;
+ }
+ // See comment in onDebugRingBufferDataAvailable(), for an explanation
+ // of why this callback is invoked without |sLock| held.
+ eventHandler.onWifiAlert(
+ errorCode, NativeUtil.byteArrayFromArrayList(debugData));
+ });
+ }
+ }
+
+ /**
+ * Hal Device Manager callbacks.
+ */
+ public class HalDeviceManagerStatusListener implements HalDeviceManager.ManagerStatusListener {
+ @Override
+ public void onStatusChanged() {
+ boolean isReady = mHalDeviceManager.isReady();
+ boolean isStarted = mHalDeviceManager.isStarted();
+
+ mVerboseLog.i("Device Manager onStatusChanged. isReady(): " + isReady
+ + ", isStarted(): " + isStarted);
+ if (!isReady) {
+ // Probably something unpleasant, e.g. the server died
+ WifiNative.VendorHalDeathEventHandler handler;
+ synchronized (sLock) {
+ clearState();
+ handler = mDeathEventHandler;
+ }
+ if (handler != null) {
+ handler.onDeath();
+ }
+ }
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/WificondControl.java b/service/java/com/android/server/wifi/WificondControl.java
new file mode 100644
index 0000000..a877c09
--- /dev/null
+++ b/service/java/com/android/server/wifi/WificondControl.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.net.wifi.IApInterface;
+import android.net.wifi.IClientInterface;
+import android.net.wifi.IPnoScanEvent;
+import android.net.wifi.IScanEvent;
+import android.net.wifi.IWifiScannerImpl;
+import android.net.wifi.IWificond;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiScanner;
+import android.net.wifi.WifiSsid;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.util.InformationElementUtil;
+import com.android.server.wifi.util.NativeUtil;
+import com.android.server.wifi.wificond.ChannelSettings;
+import com.android.server.wifi.wificond.HiddenNetwork;
+import com.android.server.wifi.wificond.NativeScanResult;
+import com.android.server.wifi.wificond.PnoNetwork;
+import com.android.server.wifi.wificond.PnoSettings;
+import com.android.server.wifi.wificond.SingleScanSettings;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * This class provides methods for WifiNative to send control commands to wificond.
+ * NOTE: This class should only be used from WifiNative.
+ */
+public class WificondControl {
+ private boolean mVerboseLoggingEnabled = false;
+
+ private static final String TAG = "WificondControl";
+ private WifiInjector mWifiInjector;
+ private WifiMonitor mWifiMonitor;
+
+ // Cached wificond binder handlers.
+ private IWificond mWificond;
+ private IClientInterface mClientInterface;
+ private IApInterface mApInterface;
+ private IWifiScannerImpl mWificondScanner;
+ private IScanEvent mScanEventHandler;
+ private IPnoScanEvent mPnoScanEventHandler;
+
+ private String mClientInterfaceName;
+
+
+ private class ScanEventHandler extends IScanEvent.Stub {
+ @Override
+ public void OnScanResultReady() {
+ Log.d(TAG, "Scan result ready event");
+ mWifiMonitor.broadcastScanResultEvent(mClientInterfaceName);
+ }
+
+ @Override
+ public void OnScanFailed() {
+ Log.d(TAG, "Scan failed event");
+ mWifiMonitor.broadcastScanFailedEvent(mClientInterfaceName);
+ }
+ }
+
+ WificondControl(WifiInjector wifiInjector, WifiMonitor wifiMonitor) {
+ mWifiInjector = wifiInjector;
+ mWifiMonitor = wifiMonitor;
+ }
+
+ private class PnoScanEventHandler extends IPnoScanEvent.Stub {
+ @Override
+ public void OnPnoNetworkFound() {
+ Log.d(TAG, "Pno scan result event");
+ mWifiMonitor.broadcastPnoScanResultEvent(mClientInterfaceName);
+ }
+
+ @Override
+ public void OnPnoScanFailed() {
+ Log.d(TAG, "Pno Scan failed event");
+ // Nothing to do for now.
+ }
+ }
+
+ /** Enable or disable verbose logging of WificondControl.
+ * @param enable True to enable verbose logging. False to disable verbose logging.
+ */
+ public void enableVerboseLogging(boolean enable) {
+ mVerboseLoggingEnabled = enable;
+ }
+
+ /**
+ * Setup driver for client mode via wificond.
+ * @return An IClientInterface as wificond client interface binder handler.
+ * Returns null on failure.
+ */
+ public IClientInterface setupDriverForClientMode() {
+ Log.d(TAG, "Setting up driver for client mode");
+ mWificond = mWifiInjector.makeWificond();
+ if (mWificond == null) {
+ Log.e(TAG, "Failed to get reference to wificond");
+ return null;
+ }
+
+ IClientInterface clientInterface = null;
+ try {
+ clientInterface = mWificond.createClientInterface();
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to get IClientInterface due to remote exception");
+ return null;
+ }
+
+ if (clientInterface == null) {
+ Log.e(TAG, "Could not get IClientInterface instance from wificond");
+ return null;
+ }
+ Binder.allowBlocking(clientInterface.asBinder());
+
+ // Refresh Handlers
+ mClientInterface = clientInterface;
+ try {
+ mClientInterfaceName = clientInterface.getInterfaceName();
+ mWificondScanner = mClientInterface.getWifiScannerImpl();
+ if (mWificondScanner == null) {
+ Log.e(TAG, "Failed to get WificondScannerImpl");
+ return null;
+ }
+ Binder.allowBlocking(mWificondScanner.asBinder());
+ mScanEventHandler = new ScanEventHandler();
+ mWificondScanner.subscribeScanEvents(mScanEventHandler);
+ mPnoScanEventHandler = new PnoScanEventHandler();
+ mWificondScanner.subscribePnoScanEvents(mPnoScanEventHandler);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to refresh wificond scanner due to remote exception");
+ }
+
+ return clientInterface;
+ }
+
+ /**
+ * Setup driver for softAp mode via wificond.
+ * @return An IApInterface as wificond Ap interface binder handler.
+ * Returns null on failure.
+ */
+ public IApInterface setupDriverForSoftApMode() {
+ Log.d(TAG, "Setting up driver for soft ap mode");
+ mWificond = mWifiInjector.makeWificond();
+ if (mWificond == null) {
+ Log.e(TAG, "Failed to get reference to wificond");
+ return null;
+ }
+
+ IApInterface apInterface = null;
+ try {
+ apInterface = mWificond.createApInterface();
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to get IApInterface due to remote exception");
+ return null;
+ }
+
+ if (apInterface == null) {
+ Log.e(TAG, "Could not get IApInterface instance from wificond");
+ return null;
+ }
+ Binder.allowBlocking(apInterface.asBinder());
+
+ // Refresh Handlers
+ mApInterface = apInterface;
+
+ return apInterface;
+ }
+
+ /**
+ * Teardown all interfaces configured in wificond.
+ * @return Returns true on success.
+ */
+ public boolean tearDownInterfaces() {
+ Log.d(TAG, "tearing down interfaces in wificond");
+ // Explicitly refresh the wificodn handler because |tearDownInterfaces()|
+ // could be used to cleanup before we setup any interfaces.
+ mWificond = mWifiInjector.makeWificond();
+ if (mWificond == null) {
+ Log.e(TAG, "Failed to get reference to wificond");
+ return false;
+ }
+
+ try {
+ if (mWificondScanner != null) {
+ mWificondScanner.unsubscribeScanEvents();
+ mWificondScanner.unsubscribePnoScanEvents();
+ }
+ mWificond.tearDownInterfaces();
+
+ // Refresh handlers
+ mClientInterface = null;
+ mWificondScanner = null;
+ mPnoScanEventHandler = null;
+ mScanEventHandler = null;
+ mApInterface = null;
+
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to tear down interfaces due to remote exception");
+ }
+
+ return false;
+ }
+
+ /**
+ * Disable wpa_supplicant via wificond.
+ * @return Returns true on success.
+ */
+ public boolean disableSupplicant() {
+ if (mClientInterface == null) {
+ Log.e(TAG, "No valid wificond client interface handler");
+ return false;
+ }
+ try {
+ return mClientInterface.disableSupplicant();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to disable supplicant due to remote exception");
+ }
+ return false;
+ }
+
+ /**
+ * Enable wpa_supplicant via wificond.
+ * @return Returns true on success.
+ */
+ public boolean enableSupplicant() {
+ if (mClientInterface == null) {
+ Log.e(TAG, "No valid wificond client interface handler");
+ return false;
+ }
+
+ try {
+ return mClientInterface.enableSupplicant();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to enable supplicant due to remote exception");
+ }
+ return false;
+ }
+
+ /**
+ * Request signal polling to wificond.
+ * Returns an SignalPollResult object.
+ * Returns null on failure.
+ */
+ public WifiNative.SignalPollResult signalPoll() {
+ if (mClientInterface == null) {
+ Log.e(TAG, "No valid wificond client interface handler");
+ return null;
+ }
+
+ int[] resultArray;
+ try {
+ resultArray = mClientInterface.signalPoll();
+ if (resultArray == null || resultArray.length != 3) {
+ Log.e(TAG, "Invalid signal poll result from wificond");
+ return null;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to do signal polling due to remote exception");
+ return null;
+ }
+ WifiNative.SignalPollResult pollResult = new WifiNative.SignalPollResult();
+ pollResult.currentRssi = resultArray[0];
+ pollResult.txBitrate = resultArray[1];
+ pollResult.associationFrequency = resultArray[2];
+ return pollResult;
+ }
+
+ /**
+ * Fetch TX packet counters on current connection from wificond.
+ * Returns an TxPacketCounters object.
+ * Returns null on failure.
+ */
+ public WifiNative.TxPacketCounters getTxPacketCounters() {
+ if (mClientInterface == null) {
+ Log.e(TAG, "No valid wificond client interface handler");
+ return null;
+ }
+
+ int[] resultArray;
+ try {
+ resultArray = mClientInterface.getPacketCounters();
+ if (resultArray == null || resultArray.length != 2) {
+ Log.e(TAG, "Invalid signal poll result from wificond");
+ return null;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to do signal polling due to remote exception");
+ return null;
+ }
+ WifiNative.TxPacketCounters counters = new WifiNative.TxPacketCounters();
+ counters.txSucceeded = resultArray[0];
+ counters.txFailed = resultArray[1];
+ return counters;
+ }
+
+ /**
+ * Fetch the latest scan result from kernel via wificond.
+ * @return Returns an ArrayList of ScanDetail.
+ * Returns an empty ArrayList on failure.
+ */
+ public ArrayList<ScanDetail> getScanResults() {
+ ArrayList<ScanDetail> results = new ArrayList<>();
+ if (mWificondScanner == null) {
+ Log.e(TAG, "No valid wificond scanner interface handler");
+ return results;
+ }
+ try {
+ NativeScanResult[] nativeResults = mWificondScanner.getScanResults();
+ for (NativeScanResult result : nativeResults) {
+ WifiSsid wifiSsid = WifiSsid.createFromByteArray(result.ssid);
+ String bssid;
+ try {
+ bssid = NativeUtil.macAddressFromByteArray(result.bssid);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + result.bssid, e);
+ continue;
+ }
+ if (bssid == null) {
+ Log.e(TAG, "Illegal null bssid");
+ continue;
+ }
+ ScanResult.InformationElement[] ies =
+ InformationElementUtil.parseInformationElements(result.infoElement);
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, result.capability);
+ String flags = capabilities.generateCapabilitiesString();
+ NetworkDetail networkDetail =
+ new NetworkDetail(bssid, ies, null, result.frequency);
+
+ if (!wifiSsid.toString().equals(networkDetail.getTrimmedSSID())) {
+ Log.e(TAG, "Inconsistent SSID on BSSID: " + bssid);
+ continue;
+ }
+ ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
+ result.signalMbm / 100, result.frequency, result.tsf, ies, null);
+ results.add(scanDetail);
+ }
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to create ScanDetail ArrayList");
+ }
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "get " + results.size() + " scan results from wificond");
+ }
+
+ return results;
+ }
+
+ /**
+ * Start a scan using wificond for the given parameters.
+ * @param freqs list of frequencies to scan for, if null scan all supported channels.
+ * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
+ * @return Returns true on success.
+ */
+ public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {
+ if (mWificondScanner == null) {
+ Log.e(TAG, "No valid wificond scanner interface handler");
+ return false;
+ }
+ SingleScanSettings settings = new SingleScanSettings();
+ settings.channelSettings = new ArrayList<>();
+ settings.hiddenNetworks = new ArrayList<>();
+
+ if (freqs != null) {
+ for (Integer freq : freqs) {
+ ChannelSettings channel = new ChannelSettings();
+ channel.frequency = freq;
+ settings.channelSettings.add(channel);
+ }
+ }
+ if (hiddenNetworkSSIDs != null) {
+ for (String ssid : hiddenNetworkSSIDs) {
+ HiddenNetwork network = new HiddenNetwork();
+ try {
+ network.ssid = NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(ssid));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + ssid, e);
+ continue;
+ }
+ settings.hiddenNetworks.add(network);
+ }
+ }
+
+ try {
+ return mWificondScanner.scan(settings);
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to request scan due to remote exception");
+ }
+ return false;
+ }
+
+ /**
+ * Start PNO scan.
+ * @param pnoSettings Pno scan configuration.
+ * @return true on success.
+ */
+ public boolean startPnoScan(WifiNative.PnoSettings pnoSettings) {
+ if (mWificondScanner == null) {
+ Log.e(TAG, "No valid wificond scanner interface handler");
+ return false;
+ }
+ PnoSettings settings = new PnoSettings();
+ settings.pnoNetworks = new ArrayList<>();
+ settings.intervalMs = pnoSettings.periodInMs;
+ settings.min2gRssi = pnoSettings.min24GHzRssi;
+ settings.min5gRssi = pnoSettings.min5GHzRssi;
+ if (pnoSettings.networkList != null) {
+ for (WifiNative.PnoNetwork network : pnoSettings.networkList) {
+ PnoNetwork condNetwork = new PnoNetwork();
+ condNetwork.isHidden = (network.flags
+ & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0;
+ try {
+ condNetwork.ssid =
+ NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(network.ssid));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + network.ssid, e);
+ continue;
+ }
+ settings.pnoNetworks.add(condNetwork);
+ }
+ }
+
+ try {
+ return mWificondScanner.startPnoScan(settings);
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to stop pno scan due to remote exception");
+ }
+ return false;
+ }
+
+ /**
+ * Stop PNO scan.
+ * @return true on success.
+ */
+ public boolean stopPnoScan() {
+ if (mWificondScanner == null) {
+ Log.e(TAG, "No valid wificond scanner interface handler");
+ return false;
+ }
+ try {
+ return mWificondScanner.stopPnoScan();
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to stop pno scan due to remote exception");
+ }
+ return false;
+ }
+
+ /**
+ * Abort ongoing single scan.
+ */
+ public void abortScan() {
+ if (mWificondScanner == null) {
+ Log.e(TAG, "No valid wificond scanner interface handler");
+ return;
+ }
+ try {
+ mWificondScanner.abortScan();
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Failed to request abortScan due to remote exception");
+ }
+ }
+
+}
diff --git a/service/java/com/android/server/wifi/WnmData.java b/service/java/com/android/server/wifi/WnmData.java
deleted file mode 100644
index 861f6dd..0000000
--- a/service/java/com/android/server/wifi/WnmData.java
+++ /dev/null
@@ -1,112 +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 java.io.IOException;
-
-/**
- * This class carries the payload of a Hotspot 2.0 Wireless Network Management (WNM) frame,
- * described in the Hotspot 2.0 spec, section 3.2.
- */
-public class WnmData {
- private static final int ESS = 1; // HS2.0 spec section 3.2.1.2, table 4
-
- private final long mBssid;
- private final String mUrl;
- private final boolean mDeauthEvent;
- private final int mMethod;
- private final boolean mEss;
- private final int mDelay;
-
- public static WnmData buildWnmData(String event) throws IOException {
- // %012x HS20-SUBSCRIPTION-REMEDIATION "%u %s", osu_method, url
- // %012x HS20-DEAUTH-IMMINENT-NOTICE "%u %u %s", code, reauth_delay, url
-
- String[] segments = event.split(" ");
- if (segments.length < 2) {
- throw new IOException("Short event");
- }
-
- switch (segments[1]) {
- case WifiMonitor.HS20_SUB_REM_STR: {
- if (segments.length != 4) {
- throw new IOException("Expected 4 segments");
- }
- return new WnmData(Long.parseLong(segments[0], 16),
- segments[3],
- Integer.parseInt(segments[2]));
- }
- case WifiMonitor.HS20_DEAUTH_STR: {
- if (segments.length != 5) {
- throw new IOException("Expected 5 segments");
- }
- int codeID = Integer.parseInt(segments[2]);
- if (codeID < 0 || codeID > ESS) {
- throw new IOException("Unknown code");
- }
- return new WnmData(Long.parseLong(segments[0], 16),
- segments[4],
- codeID == ESS,
- Integer.parseInt(segments[3]));
- }
- default:
- throw new IOException("Unknown event type");
- }
- }
-
- private WnmData(long bssid, String url, int method) {
- mBssid = bssid;
- mUrl = url;
- mMethod = method;
- mEss = false;
- mDelay = -1;
- mDeauthEvent = false;
- }
-
- private WnmData(long bssid, String url, boolean ess, int delay) {
- mBssid = bssid;
- mUrl = url;
- mEss = ess;
- mDelay = delay;
- mMethod = -1;
- mDeauthEvent = true;
- }
-
- public long getBssid() {
- return mBssid;
- }
-
- public String getUrl() {
- return mUrl;
- }
-
- public boolean isDeauthEvent() {
- return mDeauthEvent;
- }
-
- public int getMethod() {
- return mMethod;
- }
-
- public boolean isEss() {
- return mEss;
- }
-
- public int getDelay() {
- return mDelay;
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/ANQPFactory.java b/service/java/com/android/server/wifi/anqp/ANQPFactory.java
deleted file mode 100644
index 04cab55..0000000
--- a/service/java/com/android/server/wifi/anqp/ANQPFactory.java
+++ /dev/null
@@ -1,292 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import com.android.server.wifi.hotspot2.NetworkDetail;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Set;
-
-/**
- * Factory to build a collection of 802.11u ANQP elements from a byte buffer.
- */
-public class ANQPFactory {
-
- private static final List<Constants.ANQPElementType> BaseANQPSet1 = Arrays.asList(
- Constants.ANQPElementType.ANQPVenueName,
- Constants.ANQPElementType.ANQPNwkAuthType,
- Constants.ANQPElementType.ANQPRoamingConsortium,
- Constants.ANQPElementType.ANQPIPAddrAvailability,
- Constants.ANQPElementType.ANQPNAIRealm,
- Constants.ANQPElementType.ANQP3GPPNetwork,
- Constants.ANQPElementType.ANQPDomName);
-
- private static final List<Constants.ANQPElementType> BaseANQPSet2 = Arrays.asList(
- Constants.ANQPElementType.ANQPVenueName,
- Constants.ANQPElementType.ANQPNwkAuthType,
- Constants.ANQPElementType.ANQPIPAddrAvailability,
- Constants.ANQPElementType.ANQPNAIRealm,
- Constants.ANQPElementType.ANQP3GPPNetwork,
- Constants.ANQPElementType.ANQPDomName);
-
- private static final List<Constants.ANQPElementType> HS20ANQPSet = Arrays.asList(
- Constants.ANQPElementType.HSFriendlyName,
- Constants.ANQPElementType.HSWANMetrics,
- Constants.ANQPElementType.HSConnCapability);
-
- private static final List<Constants.ANQPElementType> HS20ANQPSetwOSU = Arrays.asList(
- Constants.ANQPElementType.HSFriendlyName,
- Constants.ANQPElementType.HSWANMetrics,
- Constants.ANQPElementType.HSConnCapability,
- Constants.ANQPElementType.HSOSUProviders);
-
- public static List<Constants.ANQPElementType> getBaseANQPSet(boolean includeRC) {
- return includeRC ? BaseANQPSet1 : BaseANQPSet2;
- }
-
- public static List<Constants.ANQPElementType> getHS20ANQPSet(boolean includeOSU) {
- return includeOSU ? HS20ANQPSetwOSU : HS20ANQPSet;
- }
-
- public static List<Constants.ANQPElementType> buildQueryList(NetworkDetail networkDetail,
- boolean matchSet, boolean osu) {
- List<Constants.ANQPElementType> querySet = new ArrayList<>();
-
- if (matchSet) {
- querySet.addAll(getBaseANQPSet(networkDetail.getAnqpOICount() > 0));
- }
-
- if (networkDetail.getHSRelease() != null) {
- boolean includeOSU = osu && networkDetail.getHSRelease() == NetworkDetail.HSRelease.R2;
- if (matchSet) {
- querySet.addAll(getHS20ANQPSet(includeOSU));
- }
- else if (includeOSU) {
- querySet.add(Constants.ANQPElementType.HSOSUProviders);
- }
- }
-
- return querySet;
- }
-
- public static ByteBuffer buildQueryRequest(Set<Constants.ANQPElementType> elements,
- ByteBuffer target) {
- List<Constants.ANQPElementType> list = new ArrayList<Constants.ANQPElementType>(elements);
- Collections.sort(list);
-
- ListIterator<Constants.ANQPElementType> elementIterator = list.listIterator();
-
- target.order(ByteOrder.LITTLE_ENDIAN);
- target.putShort((short) Constants.ANQP_QUERY_LIST);
- int lenPos = target.position();
- target.putShort((short) 0);
-
- while (elementIterator.hasNext()) {
- Integer id = Constants.getANQPElementID(elementIterator.next());
- if (id != null) {
- target.putShort(id.shortValue());
- } else {
- elementIterator.previous();
- break;
- }
- }
- target.putShort(lenPos, (short) (target.position() - lenPos - Constants.BYTES_IN_SHORT));
-
- // Start a new vendor specific element for HS2.0 elements:
- if (elementIterator.hasNext()) {
- target.putShort((short) Constants.ANQP_VENDOR_SPEC);
- int vsLenPos = target.position();
- target.putShort((short) 0);
-
- target.putInt(Constants.HS20_PREFIX);
- target.put((byte) Constants.HS_QUERY_LIST);
- target.put((byte) 0);
-
- while (elementIterator.hasNext()) {
- Constants.ANQPElementType elementType = elementIterator.next();
- Integer id = Constants.getHS20ElementID(elementType);
- if (id == null) {
- throw new RuntimeException("Unmapped ANQPElementType: " + elementType);
- } else {
- target.put(id.byteValue());
- }
- }
- target.putShort(vsLenPos,
- (short) (target.position() - vsLenPos - Constants.BYTES_IN_SHORT));
- }
-
- target.flip();
- return target;
- }
-
- public static ByteBuffer buildHomeRealmRequest(List<String> realmNames, ByteBuffer target) {
- target.order(ByteOrder.LITTLE_ENDIAN);
- target.putShort((short) Constants.ANQP_VENDOR_SPEC);
- int lenPos = target.position();
- target.putShort((short) 0);
-
- target.putInt(Constants.HS20_PREFIX);
- target.put((byte) Constants.HS_NAI_HOME_REALM_QUERY);
- target.put((byte) 0);
-
- target.put((byte) realmNames.size());
- for (String realmName : realmNames) {
- target.put((byte) Constants.UTF8_INDICATOR);
- byte[] octets = realmName.getBytes(StandardCharsets.UTF_8);
- target.put((byte) octets.length);
- target.put(octets);
- }
- target.putShort(lenPos, (short) (target.position() - lenPos - Constants.BYTES_IN_SHORT));
-
- target.flip();
- return target;
- }
-
- public static ByteBuffer buildIconRequest(String fileName, ByteBuffer target) {
- target.order(ByteOrder.LITTLE_ENDIAN);
- target.putShort((short) Constants.ANQP_VENDOR_SPEC);
- int lenPos = target.position();
- target.putShort((short) 0);
-
- target.putInt(Constants.HS20_PREFIX);
- target.put((byte) Constants.HS_ICON_REQUEST);
- target.put((byte) 0);
-
- target.put(fileName.getBytes(StandardCharsets.UTF_8));
- target.putShort(lenPos, (short) (target.position() - lenPos - Constants.BYTES_IN_SHORT));
-
- target.flip();
- return target;
- }
-
- public static List<ANQPElement> parsePayload(ByteBuffer payload) throws ProtocolException {
- payload.order(ByteOrder.LITTLE_ENDIAN);
- List<ANQPElement> elements = new ArrayList<ANQPElement>();
- while (payload.hasRemaining()) {
- elements.add(buildElement(payload));
- }
- return elements;
- }
-
- private static ANQPElement buildElement(ByteBuffer payload) throws ProtocolException {
- if (payload.remaining() < 4)
- throw new ProtocolException("Runt payload: " + payload.remaining());
-
- int infoIDNumber = payload.getShort() & Constants.SHORT_MASK;
- Constants.ANQPElementType infoID = Constants.mapANQPElement(infoIDNumber);
- if (infoID == null) {
- throw new ProtocolException("Bad info ID: " + infoIDNumber);
- }
- int length = payload.getShort() & Constants.SHORT_MASK;
-
- if (payload.remaining() < length) {
- throw new ProtocolException("Truncated payload: " +
- payload.remaining() + " vs " + length);
- }
- return buildElement(payload, infoID, length);
- }
-
- public static ANQPElement buildElement(ByteBuffer payload, Constants.ANQPElementType infoID,
- int length) throws ProtocolException {
- try {
- ByteBuffer elementPayload = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
- payload.position(payload.position() + length);
- elementPayload.limit(elementPayload.position() + length);
-
- switch (infoID) {
- case ANQPCapabilityList:
- return new CapabilityListElement(infoID, elementPayload);
- case ANQPVenueName:
- return new VenueNameElement(infoID, elementPayload);
- case ANQPEmergencyNumber:
- return new EmergencyNumberElement(infoID, elementPayload);
- case ANQPNwkAuthType:
- return new NetworkAuthenticationTypeElement(infoID, elementPayload);
- case ANQPRoamingConsortium:
- return new RoamingConsortiumElement(infoID, elementPayload);
- case ANQPIPAddrAvailability:
- return new IPAddressTypeAvailabilityElement(infoID, elementPayload);
- case ANQPNAIRealm:
- return new NAIRealmElement(infoID, elementPayload);
- case ANQP3GPPNetwork:
- return new ThreeGPPNetworkElement(infoID, elementPayload);
- case ANQPGeoLoc:
- return new GEOLocationElement(infoID, elementPayload);
- case ANQPCivicLoc:
- return new CivicLocationElement(infoID, elementPayload);
- case ANQPLocURI:
- return new GenericStringElement(infoID, elementPayload);
- case ANQPDomName:
- return new DomainNameElement(infoID, elementPayload);
- case ANQPEmergencyAlert:
- return new GenericStringElement(infoID, elementPayload);
- case ANQPTDLSCap:
- return new GenericBlobElement(infoID, elementPayload);
- case ANQPEmergencyNAI:
- return new GenericStringElement(infoID, elementPayload);
- case ANQPNeighborReport:
- return new GenericBlobElement(infoID, elementPayload);
- case ANQPVendorSpec:
- if (elementPayload.remaining() > 5) {
- int oi = elementPayload.getInt();
- if (oi != Constants.HS20_PREFIX) {
- return null;
- }
- int subType = elementPayload.get() & Constants.BYTE_MASK;
- Constants.ANQPElementType hs20ID = Constants.mapHS20Element(subType);
- if (hs20ID == null) {
- throw new ProtocolException("Bad HS20 info ID: " + subType);
- }
- elementPayload.get(); // Skip the reserved octet
- return buildHS20Element(hs20ID, elementPayload);
- } else {
- return new GenericBlobElement(infoID, elementPayload);
- }
- default:
- throw new ProtocolException("Unknown element ID: " + infoID);
- }
- } catch (ProtocolException e) {
- throw e;
- } catch (Exception e) {
- // TODO: remove this catch-all for exceptions, once the element parsing code
- // has been thoroughly unit tested. b/30562650
- throw new ProtocolException("Unknown parsing error", e);
- }
- }
-
- public static ANQPElement buildHS20Element(Constants.ANQPElementType infoID,
- ByteBuffer payload) throws ProtocolException {
- try {
- switch (infoID) {
- case HSCapabilityList:
- return new HSCapabilityListElement(infoID, payload);
- case HSFriendlyName:
- return new HSFriendlyNameElement(infoID, payload);
- case HSWANMetrics:
- return new HSWanMetricsElement(infoID, payload);
- case HSConnCapability:
- return new HSConnectionCapabilityElement(infoID, payload);
- case HSOperatingclass:
- return new GenericBlobElement(infoID, payload);
- case HSOSUProviders:
- return new RawByteElement(infoID, payload);
- case HSIconFile:
- return new HSIconFileElement(infoID, payload);
- default:
- return null;
- }
- } catch (ProtocolException e) {
- throw e;
- } catch (Exception e) {
- // TODO: remove this catch-all for exceptions, once the element parsing code
- // has been thoroughly unit tested. b/30562650
- throw new ProtocolException("Unknown parsing error", e);
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/CapabilityListElement.java b/service/java/com/android/server/wifi/anqp/CapabilityListElement.java
deleted file mode 100644
index 301d417..0000000
--- a/service/java/com/android/server/wifi/anqp/CapabilityListElement.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-/**
- * The ANQP Capability List element, 802.11-2012 section 8.4.4.3
- */
-public class CapabilityListElement extends ANQPElement {
- private final Constants.ANQPElementType[] mCapabilities;
-
- public CapabilityListElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
- if ((payload.remaining() & 1) == 1)
- throw new ProtocolException("Odd length");
- mCapabilities = new Constants.ANQPElementType[payload.remaining() / Constants.BYTES_IN_SHORT];
-
- int index = 0;
- while (payload.hasRemaining()) {
- int capID = payload.getShort() & Constants.SHORT_MASK;
- Constants.ANQPElementType capability = Constants.mapANQPElement(capID);
- if (capability == null)
- throw new ProtocolException("Unknown capability: " + capID);
- mCapabilities[index++] = capability;
- }
- }
-
- public Constants.ANQPElementType[] getCapabilities() {
- return mCapabilities;
- }
-
- @Override
- public String toString() {
- return "CapabilityList{" +
- "mCapabilities=" + Arrays.toString(mCapabilities) +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/CellularNetwork.java b/service/java/com/android/server/wifi/anqp/CellularNetwork.java
deleted file mode 100644
index 03d607e..0000000
--- a/service/java/com/android/server/wifi/anqp/CellularNetwork.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-
-public class CellularNetwork implements Iterable<String> {
- private static final int PLMNListType = 0;
-
- private final List<String> mMccMnc;
-
- private CellularNetwork(int plmnCount, ByteBuffer payload) throws ProtocolException {
- mMccMnc = new ArrayList<>(plmnCount);
-
- while (plmnCount > 0) {
- if (payload.remaining() < 3) {
- throw new ProtocolException("Truncated PLMN info");
- }
- byte[] plmn = new byte[3];
- payload.get(plmn);
-
- int mcc = ((plmn[0] << 8) & 0xf00) |
- (plmn[0] & 0x0f0) |
- (plmn[1] & 0x00f);
-
- int mnc = ((plmn[2] << 4) & 0xf0) |
- ((plmn[2] >> 4) & 0x0f);
-
- int n2 = (plmn[1] >> 4) & 0x0f;
- String mccMnc = n2 != 0xf ?
- String.format("%03x%03x", mcc, (mnc << 4) | n2) :
- String.format("%03x%02x", mcc, mnc);
-
- mMccMnc.add(mccMnc);
- plmnCount--;
- }
- }
-
- public static CellularNetwork buildCellularNetwork(ByteBuffer payload)
- throws ProtocolException {
- int iei = payload.get() & BYTE_MASK;
- int plmnLen = payload.get() & 0x7f;
-
- if (iei != PLMNListType) {
- payload.position(payload.position() + plmnLen);
- return null;
- }
-
- int plmnCount = payload.get() & BYTE_MASK;
- return new CellularNetwork(plmnCount, payload);
- }
-
- @Override
- public Iterator<String> iterator() {
- return mMccMnc.iterator();
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("PLMN:");
- for (String mccMnc : mMccMnc) {
- sb.append(' ').append(mccMnc);
- }
- return sb.toString();
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/CivicLocationElement.java b/service/java/com/android/server/wifi/anqp/CivicLocationElement.java
deleted file mode 100644
index 7269336..0000000
--- a/service/java/com/android/server/wifi/anqp/CivicLocationElement.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-/**
- * The Civic Location ANQP Element, IEEE802.11-2012 section 8.4.4.13
- */
-public class CivicLocationElement extends ANQPElement {
- public enum LocationType {DHCPServer, NwkElement, Client}
-
- private static final int GEOCONF_CIVIC4 = 99;
- private static final int RFC4776 = 0; // Table 8-77, 1=vendor specific
-
- private final LocationType mLocationType;
- private final Locale mLocale;
- private final Map<CAType, String> mValues;
-
- public CivicLocationElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- if (payload.remaining() < 6) {
- throw new ProtocolException("Runt civic location:" + payload.remaining());
- }
-
- int locType = payload.get() & Constants.BYTE_MASK;
- if (locType != RFC4776) {
- throw new ProtocolException("Bad Civic location type: " + locType);
- }
-
- int locSubType = payload.get() & Constants.BYTE_MASK;
- if (locSubType != GEOCONF_CIVIC4) {
- throw new ProtocolException("Unexpected Civic location sub-type: " + locSubType +
- " (cannot handle sub elements)");
- }
-
- int length = payload.get() & Constants.BYTE_MASK;
- if (length > payload.remaining()) {
- throw new ProtocolException("Invalid CA type length: " + length);
- }
-
- int what = payload.get() & Constants.BYTE_MASK;
- mLocationType = what < LocationType.values().length ? LocationType.values()[what] : null;
-
- mLocale = Locale.forLanguageTag(Constants.getString(payload, 2, StandardCharsets.US_ASCII));
-
- mValues = new HashMap<CAType, String>();
- while (payload.hasRemaining()) {
- int caTypeNumber = payload.get() & Constants.BYTE_MASK;
- CAType caType = s_caTypes.get(caTypeNumber);
-
- int caValLen = payload.get() & Constants.BYTE_MASK;
- if (caValLen > payload.remaining()) {
- throw new ProtocolException("Bad CA value length: " + caValLen);
- }
- byte[] caValOctets = new byte[caValLen];
- payload.get(caValOctets);
-
- if (caType != null) {
- mValues.put(caType, new String(caValOctets, StandardCharsets.UTF_8));
- }
- }
- }
-
- public LocationType getLocationType() {
- return mLocationType;
- }
-
- public Locale getLocale() {
- return mLocale;
- }
-
- public Map<CAType, String> getValues() {
- return Collections.unmodifiableMap(mValues);
- }
-
- @Override
- public String toString() {
- return "CivicLocation{" +
- "mLocationType=" + mLocationType +
- ", mLocale=" + mLocale +
- ", mValues=" + mValues +
- '}';
- }
-
- private static final Map<Integer, CAType> s_caTypes = new HashMap<Integer, CAType>();
-
- public static final int LANGUAGE = 0;
- public static final int STATE_PROVINCE = 1;
- public static final int COUNTY_DISTRICT = 2;
- public static final int CITY = 3;
- public static final int DIVISION_BOROUGH = 4;
- public static final int BLOCK = 5;
- public static final int STREET_GROUP = 6;
- public static final int STREET_DIRECTION = 16;
- public static final int LEADING_STREET_SUFFIX = 17;
- public static final int STREET_SUFFIX = 18;
- public static final int HOUSE_NUMBER = 19;
- public static final int HOUSE_NUMBER_SUFFIX = 20;
- public static final int LANDMARK = 21;
- public static final int ADDITIONAL_LOCATION = 22;
- public static final int NAME = 23;
- public static final int POSTAL_ZIP = 24;
- public static final int BUILDING = 25;
- public static final int UNIT = 26;
- public static final int FLOOR = 27;
- public static final int ROOM = 28;
- public static final int TYPE = 29;
- public static final int POSTAL_COMMUNITY = 30;
- public static final int PO_BOX = 31;
- public static final int ADDITIONAL_CODE = 32;
- public static final int SEAT_DESK = 33;
- public static final int PRIMARY_ROAD = 34;
- public static final int ROAD_SECTION = 35;
- public static final int BRANCH_ROAD = 36;
- public static final int SUB_BRANCH_ROAD = 37;
- public static final int STREET_NAME_PRE_MOD = 38;
- public static final int STREET_NAME_POST_MOD = 39;
- public static final int SCRIPT = 128;
- public static final int RESERVED = 255;
-
- public enum CAType {
- Language,
- StateProvince,
- CountyDistrict,
- City,
- DivisionBorough,
- Block,
- StreetGroup,
- StreetDirection,
- LeadingStreetSuffix,
- StreetSuffix,
- HouseNumber,
- HouseNumberSuffix,
- Landmark,
- AdditionalLocation,
- Name,
- PostalZIP,
- Building,
- Unit,
- Floor,
- Room,
- Type,
- PostalCommunity,
- POBox,
- AdditionalCode,
- SeatDesk,
- PrimaryRoad,
- RoadSection,
- BranchRoad,
- SubBranchRoad,
- StreetNamePreMod,
- StreetNamePostMod,
- Script,
- Reserved
- }
-
- static {
- s_caTypes.put(LANGUAGE, CAType.Language);
- s_caTypes.put(STATE_PROVINCE, CAType.StateProvince);
- s_caTypes.put(COUNTY_DISTRICT, CAType.CountyDistrict);
- s_caTypes.put(CITY, CAType.City);
- s_caTypes.put(DIVISION_BOROUGH, CAType.DivisionBorough);
- s_caTypes.put(BLOCK, CAType.Block);
- s_caTypes.put(STREET_GROUP, CAType.StreetGroup);
- s_caTypes.put(STREET_DIRECTION, CAType.StreetDirection);
- s_caTypes.put(LEADING_STREET_SUFFIX, CAType.LeadingStreetSuffix);
- s_caTypes.put(STREET_SUFFIX, CAType.StreetSuffix);
- s_caTypes.put(HOUSE_NUMBER, CAType.HouseNumber);
- s_caTypes.put(HOUSE_NUMBER_SUFFIX, CAType.HouseNumberSuffix);
- s_caTypes.put(LANDMARK, CAType.Landmark);
- s_caTypes.put(ADDITIONAL_LOCATION, CAType.AdditionalLocation);
- s_caTypes.put(NAME, CAType.Name);
- s_caTypes.put(POSTAL_ZIP, CAType.PostalZIP);
- s_caTypes.put(BUILDING, CAType.Building);
- s_caTypes.put(UNIT, CAType.Unit);
- s_caTypes.put(FLOOR, CAType.Floor);
- s_caTypes.put(ROOM, CAType.Room);
- s_caTypes.put(TYPE, CAType.Type);
- s_caTypes.put(POSTAL_COMMUNITY, CAType.PostalCommunity);
- s_caTypes.put(PO_BOX, CAType.POBox);
- s_caTypes.put(ADDITIONAL_CODE, CAType.AdditionalCode);
- s_caTypes.put(SEAT_DESK, CAType.SeatDesk);
- s_caTypes.put(PRIMARY_ROAD, CAType.PrimaryRoad);
- s_caTypes.put(ROAD_SECTION, CAType.RoadSection);
- s_caTypes.put(BRANCH_ROAD, CAType.BranchRoad);
- s_caTypes.put(SUB_BRANCH_ROAD, CAType.SubBranchRoad);
- s_caTypes.put(STREET_NAME_PRE_MOD, CAType.StreetNamePreMod);
- s_caTypes.put(STREET_NAME_POST_MOD, CAType.StreetNamePostMod);
- s_caTypes.put(SCRIPT, CAType.Script);
- s_caTypes.put(RESERVED, CAType.Reserved);
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/Constants.java b/service/java/com/android/server/wifi/anqp/Constants.java
deleted file mode 100644
index c4a10cd..0000000
--- a/service/java/com/android/server/wifi/anqp/Constants.java
+++ /dev/null
@@ -1,233 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.util.Collection;
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * ANQP related constants (802.11-2012)
- */
-public class Constants {
-
- public static final int NIBBLE_MASK = 0x0f;
- public static final int BYTE_MASK = 0xff;
- public static final int SHORT_MASK = 0xffff;
- public static final long INT_MASK = 0xffffffffL;
- public static final int BYTES_IN_SHORT = 2;
- public static final int BYTES_IN_INT = 4;
- public static final int BYTES_IN_EUI48 = 6;
- public static final long MILLIS_IN_A_SEC = 1000L;
-
- public static final int HS20_PREFIX = 0x119a6f50; // Note that this is represented as a LE int
- public static final int HS20_FRAME_PREFIX = 0x109a6f50;
- public static final int UTF8_INDICATOR = 1;
-
- public static final int LANG_CODE_LENGTH = 3;
-
- public static final int ANQP_QUERY_LIST = 256;
- public static final int ANQP_CAPABILITY_LIST = 257;
- public static final int ANQP_VENUE_NAME = 258;
- public static final int ANQP_EMERGENCY_NUMBER = 259;
- public static final int ANQP_NWK_AUTH_TYPE = 260;
- public static final int ANQP_ROAMING_CONSORTIUM = 261;
- public static final int ANQP_IP_ADDR_AVAILABILITY = 262;
- public static final int ANQP_NAI_REALM = 263;
- public static final int ANQP_3GPP_NETWORK = 264;
- public static final int ANQP_GEO_LOC = 265;
- public static final int ANQP_CIVIC_LOC = 266;
- public static final int ANQP_LOC_URI = 267;
- public static final int ANQP_DOM_NAME = 268;
- public static final int ANQP_EMERGENCY_ALERT = 269;
- public static final int ANQP_TDLS_CAP = 270;
- public static final int ANQP_EMERGENCY_NAI = 271;
- public static final int ANQP_NEIGHBOR_REPORT = 272;
- public static final int ANQP_VENDOR_SPEC = 56797;
-
- public static final int HS_QUERY_LIST = 1;
- public static final int HS_CAPABILITY_LIST = 2;
- public static final int HS_FRIENDLY_NAME = 3;
- public static final int HS_WAN_METRICS = 4;
- public static final int HS_CONN_CAPABILITY = 5;
- public static final int HS_NAI_HOME_REALM_QUERY = 6;
- public static final int HS_OPERATING_CLASS = 7;
- public static final int HS_OSU_PROVIDERS = 8;
- public static final int HS_ICON_REQUEST = 10;
- public static final int HS_ICON_FILE = 11;
-
- public enum ANQPElementType {
- ANQPQueryList,
- ANQPCapabilityList,
- ANQPVenueName,
- ANQPEmergencyNumber,
- ANQPNwkAuthType,
- ANQPRoamingConsortium,
- ANQPIPAddrAvailability,
- ANQPNAIRealm,
- ANQP3GPPNetwork,
- ANQPGeoLoc,
- ANQPCivicLoc,
- ANQPLocURI,
- ANQPDomName,
- ANQPEmergencyAlert,
- ANQPTDLSCap,
- ANQPEmergencyNAI,
- ANQPNeighborReport,
- ANQPVendorSpec,
- HSQueryList,
- HSCapabilityList,
- HSFriendlyName,
- HSWANMetrics,
- HSConnCapability,
- HSNAIHomeRealmQuery,
- HSOperatingclass,
- HSOSUProviders,
- HSIconRequest,
- HSIconFile
- }
-
- private static final Map<Integer, ANQPElementType> sAnqpMap = new HashMap<>();
- private static final Map<Integer, ANQPElementType> sHs20Map = new HashMap<>();
- private static final Map<ANQPElementType, Integer> sRevAnqpmap =
- new EnumMap<>(ANQPElementType.class);
- private static final Map<ANQPElementType, Integer> sRevHs20map =
- new EnumMap<>(ANQPElementType.class);
-
- static {
- sAnqpMap.put(ANQP_QUERY_LIST, ANQPElementType.ANQPQueryList);
- sAnqpMap.put(ANQP_CAPABILITY_LIST, ANQPElementType.ANQPCapabilityList);
- sAnqpMap.put(ANQP_VENUE_NAME, ANQPElementType.ANQPVenueName);
- sAnqpMap.put(ANQP_EMERGENCY_NUMBER, ANQPElementType.ANQPEmergencyNumber);
- sAnqpMap.put(ANQP_NWK_AUTH_TYPE, ANQPElementType.ANQPNwkAuthType);
- sAnqpMap.put(ANQP_ROAMING_CONSORTIUM, ANQPElementType.ANQPRoamingConsortium);
- sAnqpMap.put(ANQP_IP_ADDR_AVAILABILITY, ANQPElementType.ANQPIPAddrAvailability);
- sAnqpMap.put(ANQP_NAI_REALM, ANQPElementType.ANQPNAIRealm);
- sAnqpMap.put(ANQP_3GPP_NETWORK, ANQPElementType.ANQP3GPPNetwork);
- sAnqpMap.put(ANQP_GEO_LOC, ANQPElementType.ANQPGeoLoc);
- sAnqpMap.put(ANQP_CIVIC_LOC, ANQPElementType.ANQPCivicLoc);
- sAnqpMap.put(ANQP_LOC_URI, ANQPElementType.ANQPLocURI);
- sAnqpMap.put(ANQP_DOM_NAME, ANQPElementType.ANQPDomName);
- sAnqpMap.put(ANQP_EMERGENCY_ALERT, ANQPElementType.ANQPEmergencyAlert);
- sAnqpMap.put(ANQP_TDLS_CAP, ANQPElementType.ANQPTDLSCap);
- sAnqpMap.put(ANQP_EMERGENCY_NAI, ANQPElementType.ANQPEmergencyNAI);
- sAnqpMap.put(ANQP_NEIGHBOR_REPORT, ANQPElementType.ANQPNeighborReport);
- sAnqpMap.put(ANQP_VENDOR_SPEC, ANQPElementType.ANQPVendorSpec);
-
- sHs20Map.put(HS_QUERY_LIST, ANQPElementType.HSQueryList);
- sHs20Map.put(HS_CAPABILITY_LIST, ANQPElementType.HSCapabilityList);
- sHs20Map.put(HS_FRIENDLY_NAME, ANQPElementType.HSFriendlyName);
- sHs20Map.put(HS_WAN_METRICS, ANQPElementType.HSWANMetrics);
- sHs20Map.put(HS_CONN_CAPABILITY, ANQPElementType.HSConnCapability);
- sHs20Map.put(HS_NAI_HOME_REALM_QUERY, ANQPElementType.HSNAIHomeRealmQuery);
- sHs20Map.put(HS_OPERATING_CLASS, ANQPElementType.HSOperatingclass);
- sHs20Map.put(HS_OSU_PROVIDERS, ANQPElementType.HSOSUProviders);
- sHs20Map.put(HS_ICON_REQUEST, ANQPElementType.HSIconRequest);
- sHs20Map.put(HS_ICON_FILE, ANQPElementType.HSIconFile);
-
- for (Map.Entry<Integer, ANQPElementType> entry : sAnqpMap.entrySet()) {
- sRevAnqpmap.put(entry.getValue(), entry.getKey());
- }
- for (Map.Entry<Integer, ANQPElementType> entry : sHs20Map.entrySet()) {
- sRevHs20map.put(entry.getValue(), entry.getKey());
- }
- }
-
- public static ANQPElementType mapANQPElement(int id) {
- return sAnqpMap.get(id);
- }
-
- public static ANQPElementType mapHS20Element(int id) {
- return sHs20Map.get(id);
- }
-
- public static Integer getANQPElementID(ANQPElementType elementType) {
- return sRevAnqpmap.get(elementType);
- }
-
- public static Integer getHS20ElementID(ANQPElementType elementType) {
- return sRevHs20map.get(elementType);
- }
-
- public static boolean hasBaseANQPElements(Collection<ANQPElementType> elements) {
- if (elements == null) {
- return false;
- }
- for (ANQPElementType element : elements) {
- if (sRevAnqpmap.containsKey(element)) {
- return true;
- }
- }
- return false;
- }
-
- public static boolean hasR2Elements(List<ANQPElementType> elements) {
- return elements.contains(ANQPElementType.HSOSUProviders);
- }
-
- public static long getInteger(ByteBuffer payload, ByteOrder bo, int size) {
- byte[] octets = new byte[size];
- payload.get(octets);
- long value = 0;
- if (bo == ByteOrder.LITTLE_ENDIAN) {
- for (int n = octets.length - 1; n >= 0; n--) {
- value = (value << Byte.SIZE) | (octets[n] & BYTE_MASK);
- }
- }
- else {
- for (byte octet : octets) {
- value = (value << Byte.SIZE) | (octet & BYTE_MASK);
- }
- }
- return value;
- }
-
- public static String getPrefixedString(ByteBuffer payload, int lengthLength, Charset charset)
- throws ProtocolException {
- return getPrefixedString(payload, lengthLength, charset, false);
- }
-
- public static String getPrefixedString(ByteBuffer payload, int lengthLength, Charset charset,
- boolean useNull) throws ProtocolException {
- if (payload.remaining() < lengthLength) {
- throw new ProtocolException("Runt string: " + payload.remaining());
- }
- return getString(payload, (int) getInteger(payload, ByteOrder.LITTLE_ENDIAN,
- lengthLength), charset, useNull);
- }
-
- public static String getTrimmedString(ByteBuffer payload, int length, Charset charset)
- throws ProtocolException {
- String s = getString(payload, length, charset, false);
- int zero = length - 1;
- while (zero >= 0) {
- if (s.charAt(zero) != 0) {
- break;
- }
- zero--;
- }
- return zero < length - 1 ? s.substring(0, zero + 1) : s;
- }
-
- public static String getString(ByteBuffer payload, int length, Charset charset)
- throws ProtocolException {
- return getString(payload, length, charset, false);
- }
-
- public static String getString(ByteBuffer payload, int length, Charset charset, boolean useNull)
- throws ProtocolException {
- if (length > payload.remaining()) {
- throw new ProtocolException("Bad string length: " + length);
- }
- if (useNull && length == 0) {
- return null;
- }
- byte[] octets = new byte[length];
- payload.get(octets);
- return new String(octets, charset);
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/DomainNameElement.java b/service/java/com/android/server/wifi/anqp/DomainNameElement.java
deleted file mode 100644
index 69edc90..0000000
--- a/service/java/com/android/server/wifi/anqp/DomainNameElement.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The Domain Name ANQP Element, IEEE802.11-2012 section 8.4.4.15
- */
-public class DomainNameElement extends ANQPElement {
- private final List<String> mDomains;
-
- public DomainNameElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
- mDomains = new ArrayList<>();
-
- while (payload.hasRemaining()) {
- // Use latin-1 to decode for now - safe for ASCII and retains encoding
- mDomains.add(Constants.getPrefixedString(payload, 1, StandardCharsets.ISO_8859_1));
- }
- }
-
- public List<String> getDomains() {
- return Collections.unmodifiableList(mDomains);
- }
-
- @Override
- public String toString() {
- return "DomainName{" +
- "mDomains=" + mDomains +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/EmergencyNumberElement.java b/service/java/com/android/server/wifi/anqp/EmergencyNumberElement.java
deleted file mode 100644
index 6954480..0000000
--- a/service/java/com/android/server/wifi/anqp/EmergencyNumberElement.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * The Emergency Number ANQP Element, IEEE802.11-2012 section 8.4.4.5
- */
-public class EmergencyNumberElement extends ANQPElement {
- private final List<String> mNumbers;
-
- public EmergencyNumberElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- mNumbers = new ArrayList<String>();
-
- while (payload.hasRemaining()) {
- mNumbers.add(Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8));
- }
- }
-
- public List<String> getNumbers() {
- return mNumbers;
- }
-
- @Override
- public String toString() {
- return "EmergencyNumber{" +
- "mNumbers=" + mNumbers +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/GEOLocationElement.java b/service/java/com/android/server/wifi/anqp/GEOLocationElement.java
deleted file mode 100644
index d434c73..0000000
--- a/service/java/com/android/server/wifi/anqp/GEOLocationElement.java
+++ /dev/null
@@ -1,311 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-
-/**
- * Holds an AP Geospatial Location ANQP Element, as specified in IEEE802.11-2012 section
- * 8.4.4.12.
- * <p/>
- * <p>
- * Section 8.4.2.24.10 of the IEEE802.11-2012 specification refers to RFC-3825 for the format of the
- * Geospatial location information. RFC-3825 has subsequently been obsoleted by RFC-6225 which
- * defines the same basic binary format for the DHCPv4 payload except that a few unused bits of the
- * Datum field have been reserved for other uses.
- * </p>
- * <p/>
- * <p>
- * RFC-3825 defines a resolution field for each of latitude, longitude and altitude as "the number
- * of significant bits" of precision in the respective values and implies through examples and
- * otherwise that the non-significant bits should be simply disregarded and the range of values are
- * calculated as the numeric interval obtained by varying the range of "insignificant bits" between
- * its extremes. As a simple example, consider the value 33 as a simple 8-bit number with three
- * significant bits: 33 is 00100001 binary and the leading 001 are the significant bits. With the
- * above definition, the range of numbers are [32,63] with 33 asymmetrically located at the low end
- * of the interval. In a more realistic setting an instrument, such as a GPS, would most likely
- * deliver measurements with a gaussian distribution around the exact value, meaning it is more
- * reasonable to assume the value as a "center" value with a symmetric uncertainty interval.
- * RFC-6225 redefines the "resolution" from RFC-3825 with an "uncertainty" value with these
- * properties, which is also the definition suggested here.
- * </p>
- * <p/>
- * <p>
- * The res fields provides the resolution as the exponent to a power of two,
- * e.g. 8 means 2^8 = +/- 256, 0 means 2^0 = +/- 1 and -7 means 2^-7 +/- 0.00781250.
- * Unknown resolution is indicated by not setting the respective resolution field in the RealValue.
- * </p>
- */
-public class GEOLocationElement extends ANQPElement {
- public enum AltitudeType {Unknown, Meters, Floors}
-
- public enum Datum {Unknown, WGS84, NAD83Land, NAD83Water}
-
- private static final int ELEMENT_ID = 123; // ???
- private static final int GEO_LOCATION_LENGTH = 16;
-
- private static final int LL_FRACTION_SIZE = 25;
- private static final int LL_WIDTH = 34;
- private static final int ALT_FRACTION_SIZE = 8;
- private static final int ALT_WIDTH = 30;
- private static final int RES_WIDTH = 6;
- private static final int ALT_TYPE_WIDTH = 4;
- private static final int DATUM_WIDTH = 8;
-
- private final RealValue mLatitude;
- private final RealValue mLongitude;
- private final RealValue mAltitude;
- private final AltitudeType mAltitudeType;
- private final Datum mDatum;
-
- public static class RealValue {
- private final double mValue;
- private final boolean mResolutionSet;
- private final int mResolution;
-
- public RealValue(double value) {
- mValue = value;
- mResolution = Integer.MIN_VALUE;
- mResolutionSet = false;
- }
-
- public RealValue(double value, int resolution) {
- mValue = value;
- mResolution = resolution;
- mResolutionSet = true;
- }
-
- public double getValue() {
- return mValue;
- }
-
- public boolean isResolutionSet() {
- return mResolutionSet;
- }
-
- public int getResolution() {
- return mResolution;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(String.format("%f", mValue));
- if (mResolutionSet) {
- sb.append("+/-2^").append(mResolution);
- }
- return sb.toString();
- }
- }
-
- public GEOLocationElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- payload.get();
- int locLength = payload.get() & Constants.BYTE_MASK;
-
- if (locLength != GEO_LOCATION_LENGTH) {
- throw new ProtocolException("GeoLocation length field value " + locLength +
- " incorrect, expected 16");
- }
- if (payload.remaining() != GEO_LOCATION_LENGTH) {
- throw new ProtocolException("Bad buffer length " + payload.remaining() +
- ", expected 16");
- }
-
- ReverseBitStream reverseBitStream = new ReverseBitStream(payload);
-
- int rawLatRes = (int) reverseBitStream.sliceOff(RES_WIDTH);
- double latitude =
- fixToFloat(reverseBitStream.sliceOff(LL_WIDTH), LL_FRACTION_SIZE, LL_WIDTH);
-
- mLatitude = rawLatRes != 0 ?
- new RealValue(latitude, bitsToAbsResolution(rawLatRes, LL_WIDTH,
- LL_FRACTION_SIZE)) :
- new RealValue(latitude);
-
- int rawLonRes = (int) reverseBitStream.sliceOff(RES_WIDTH);
- double longitude =
- fixToFloat(reverseBitStream.sliceOff(LL_WIDTH), LL_FRACTION_SIZE, LL_WIDTH);
-
- mLongitude = rawLonRes != 0 ?
- new RealValue(longitude, bitsToAbsResolution(rawLonRes, LL_WIDTH,
- LL_FRACTION_SIZE)) :
- new RealValue(longitude);
-
- int altType = (int) reverseBitStream.sliceOff(ALT_TYPE_WIDTH);
- mAltitudeType = altType < AltitudeType.values().length ?
- AltitudeType.values()[altType] :
- AltitudeType.Unknown;
-
- int rawAltRes = (int) reverseBitStream.sliceOff(RES_WIDTH);
- double altitude = fixToFloat(reverseBitStream.sliceOff(ALT_WIDTH), ALT_FRACTION_SIZE,
- ALT_WIDTH);
-
- mAltitude = rawAltRes != 0 ?
- new RealValue(altitude, bitsToAbsResolution(rawAltRes, ALT_WIDTH,
- ALT_FRACTION_SIZE)) :
- new RealValue(altitude);
-
- int datumValue = (int) reverseBitStream.sliceOff(DATUM_WIDTH);
- mDatum = datumValue < Datum.values().length ? Datum.values()[datumValue] : Datum.Unknown;
- }
-
- public RealValue getLatitude() {
- return mLatitude;
- }
-
- public RealValue getLongitude() {
- return mLongitude;
- }
-
- public RealValue getAltitude() {
- return mAltitude;
- }
-
- public AltitudeType getAltitudeType() {
- return mAltitudeType;
- }
-
- public Datum getDatum() {
- return mDatum;
- }
-
- @Override
- public String toString() {
- return "GEOLocation{" +
- "mLatitude=" + mLatitude +
- ", mLongitude=" + mLongitude +
- ", mAltitude=" + mAltitude +
- ", mAltitudeType=" + mAltitudeType +
- ", mDatum=" + mDatum +
- '}';
- }
-
- private static class ReverseBitStream {
-
- private final byte[] mOctets;
- private int mBitoffset;
-
- private ReverseBitStream(ByteBuffer octets) {
- mOctets = new byte[octets.remaining()];
- octets.get(mOctets);
- }
-
- private long sliceOff(int bits) {
- final int bn = mBitoffset + bits;
- int remaining = bits;
- long value = 0;
-
- while (mBitoffset < bn) {
- int sbit = mBitoffset & 0x7; // Bit #0 is MSB, inclusive
- int octet = mBitoffset >>> 3;
-
- // Copy the minimum of what's to the right of sbit
- // and how much more goes to the target
- int width = Math.min(Byte.SIZE - sbit, remaining);
-
- value = (value << width) | getBits(mOctets[octet], sbit, width);
-
- mBitoffset += width;
- remaining -= width;
- }
-
- return value;
- }
-
- private static int getBits(byte b, int b0, int width) {
- int mask = (1 << width) - 1;
- return (b >> (Byte.SIZE - b0 - width)) & mask;
- }
- }
-
- private static class BitStream {
-
- private final byte[] data;
- private int bitOffset; // bit 0 is MSB of data[0]
-
- private BitStream(int octets) {
- data = new byte[octets];
- }
-
- private void append(long value, int width) {
- System.out.printf("Appending %x:%d\n", value, width);
- for (int sbit = width - 1; sbit >= 0; ) {
- int b0 = bitOffset >>> 3;
- int dbit = bitOffset & 0x7;
-
- int shr = sbit - 7 + dbit;
- int dmask = 0xff >>> dbit;
-
- if (shr >= 0) {
- data[b0] = (byte) ((data[b0] & ~dmask) | ((value >>> shr) & dmask));
- bitOffset += Byte.SIZE - dbit;
- sbit -= Byte.SIZE - dbit;
- } else {
- data[b0] = (byte) ((data[b0] & ~dmask) | ((value << -shr) & dmask));
- bitOffset += sbit + 1;
- sbit = -1;
- }
- }
- }
-
- private byte[] getOctets() {
- return data;
- }
- }
-
- static double fixToFloat(long value, int fractionSize, int width) {
- long sign = 1L << (width - 1);
- if ((value & sign) != 0) {
- value = -value;
- return -(double) (value & (sign - 1)) / (double) (1L << fractionSize);
- } else {
- return (double) (value & (sign - 1)) / (double) (1L << fractionSize);
- }
- }
-
- private static long floatToFix(double value, int fractionSize, int width) {
- return Math.round(value * (1L << fractionSize)) & ((1L << width) - 1);
- }
-
- private static final double LOG2_FACTOR = 1.0 / Math.log(2.0);
-
- /**
- * Convert an absolute variance value into absolute resolution representation,
- * where the variance = 2^resolution.
- *
- * @param variance The absolute variance
- * @return the absolute resolution.
- */
- private static int getResolution(double variance) {
- return (int) Math.ceil(Math.log(variance) * LOG2_FACTOR);
- }
-
- /**
- * Convert an absolute resolution, into the "number of significant bits" for the given fixed
- * point notation as defined in RFC-3825 and refined in RFC-6225.
- *
- * @param resolution absolute resolution given as 2^resolution.
- * @param fieldWidth Full width of the fixed point number used to represent the value.
- * @param fractionBits Number of fraction bits in the fixed point number used to represent the
- * value.
- * @return The number of "significant bits".
- */
- private static int absResolutionToBits(int resolution, int fieldWidth, int fractionBits) {
- return fieldWidth - fractionBits - 1 - resolution;
- }
-
- /**
- * Convert the protocol definition of "number of significant bits" into an absolute resolution.
- *
- * @param bits The number of "significant bits" from the binary protocol.
- * @param fieldWidth Full width of the fixed point number used to represent the value.
- * @param fractionBits Number of fraction bits in the fixed point number used to represent the
- * value.
- * @return The absolute resolution given as 2^resolution.
- */
- private static int bitsToAbsResolution(long bits, int fieldWidth, int fractionBits) {
- return fieldWidth - fractionBits - 1 - (int) bits;
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/GenericStringElement.java b/service/java/com/android/server/wifi/anqp/GenericStringElement.java
deleted file mode 100644
index 6cf7b1a..0000000
--- a/service/java/com/android/server/wifi/anqp/GenericStringElement.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-
-/**
- * ANQP Element to hold a generic (UTF-8 decoded) character string
- */
-public class GenericStringElement extends ANQPElement {
- private final String mText;
-
- public GenericStringElement(Constants.ANQPElementType infoID, ByteBuffer payload) throws ProtocolException {
- super(infoID);
- mText = Constants.getString(payload, payload.remaining(), StandardCharsets.UTF_8);
- }
-
- public String getM_text() {
- return mText;
- }
-
- @Override
- public String toString() {
- return "Element ID " + getID() + ": '" + mText + "'";
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/HSCapabilityListElement.java b/service/java/com/android/server/wifi/anqp/HSCapabilityListElement.java
deleted file mode 100644
index 8269324..0000000
--- a/service/java/com/android/server/wifi/anqp/HSCapabilityListElement.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-/**
- * The HS Capability list vendor specific ANQP Element,
- * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
- * section 4.2
- */
-public class HSCapabilityListElement extends ANQPElement {
- private final Constants.ANQPElementType[] mCapabilities;
-
- public HSCapabilityListElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- mCapabilities = new Constants.ANQPElementType[payload.remaining()];
-
- int index = 0;
- while (payload.hasRemaining()) {
- int capID = payload.get() & Constants.BYTE_MASK;
- Constants.ANQPElementType capability = Constants.mapHS20Element(capID);
- if (capability == null) {
- throw new ProtocolException("Unknown capability: " + capID);
- }
- mCapabilities[index++] = capability;
- }
- }
-
- public Constants.ANQPElementType[] getCapabilities() {
- return mCapabilities;
- }
-
- @Override
- public String toString() {
- return "HSCapabilityList{" +
- "mCapabilities=" + Arrays.toString(mCapabilities) +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/HSConnectionCapabilityElement.java b/service/java/com/android/server/wifi/anqp/HSConnectionCapabilityElement.java
deleted file mode 100644
index 53f1051..0000000
--- a/service/java/com/android/server/wifi/anqp/HSConnectionCapabilityElement.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The Connection Capability vendor specific ANQP Element,
- * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
- * section 4.5
- */
-public class HSConnectionCapabilityElement extends ANQPElement {
-
- public enum ProtoStatus {Closed, Open, Unknown}
-
- private final List<ProtocolTuple> mStatusList;
-
- public static class ProtocolTuple {
- private final int mProtocol;
- private final int mPort;
- private final ProtoStatus mStatus;
-
- private ProtocolTuple(ByteBuffer payload) throws ProtocolException {
- if (payload.remaining() < 4) {
- throw new ProtocolException("Runt protocol tuple: " + payload.remaining());
- }
- mProtocol = payload.get() & Constants.BYTE_MASK;
- mPort = payload.getShort() & Constants.SHORT_MASK;
- int statusNumber = payload.get() & Constants.BYTE_MASK;
- mStatus = statusNumber < ProtoStatus.values().length ?
- ProtoStatus.values()[statusNumber] :
- null;
- }
-
- public int getProtocol() {
- return mProtocol;
- }
-
- public int getPort() {
- return mPort;
- }
-
- public ProtoStatus getStatus() {
- return mStatus;
- }
-
- @Override
- public String toString() {
- return "ProtocolTuple{" +
- "mProtocol=" + mProtocol +
- ", mPort=" + mPort +
- ", mStatus=" + mStatus +
- '}';
- }
- }
-
- public HSConnectionCapabilityElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- mStatusList = new ArrayList<>();
- while (payload.hasRemaining()) {
- mStatusList.add(new ProtocolTuple(payload));
- }
- }
-
- public List<ProtocolTuple> getStatusList() {
- return Collections.unmodifiableList(mStatusList);
- }
-
- @Override
- public String toString() {
- return "HSConnectionCapability{" +
- "mStatusList=" + mStatusList +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/HSFriendlyNameElement.java b/service/java/com/android/server/wifi/anqp/HSFriendlyNameElement.java
deleted file mode 100644
index a15fc29..0000000
--- a/service/java/com/android/server/wifi/anqp/HSFriendlyNameElement.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The Operator Friendly Name vendor specific ANQP Element,
- * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
- * section 4.3
- */
-public class HSFriendlyNameElement extends ANQPElement {
- private final List<I18Name> mNames;
-
- public HSFriendlyNameElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- mNames = new ArrayList<I18Name>();
-
- while (payload.hasRemaining()) {
- mNames.add(new I18Name(payload));
- }
- }
-
- public List<I18Name> getNames() {
- return Collections.unmodifiableList(mNames);
- }
-
- @Override
- public String toString() {
- return "HSFriendlyName{" +
- "mNames=" + mNames +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/HSIconFileElement.java b/service/java/com/android/server/wifi/anqp/HSIconFileElement.java
deleted file mode 100644
index f72ab10..0000000
--- a/service/java/com/android/server/wifi/anqp/HSIconFileElement.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
-
-/**
- * The Icon Binary File vendor specific ANQP Element,
- * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
- * section 4.11
- */
-public class HSIconFileElement extends ANQPElement {
-
- public enum StatusCode {Success, FileNotFound, Unspecified}
-
- private final StatusCode mStatusCode;
- private final String mType;
- private final byte[] mIconData;
-
- public HSIconFileElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- if (payload.remaining() < 4) {
- throw new ProtocolException("Truncated icon file: " + payload.remaining());
- }
-
- int statusID = payload.get() & BYTE_MASK;
- mStatusCode = statusID < StatusCode.values().length ? StatusCode.values()[statusID] : null;
- mType = Constants.getPrefixedString(payload, 1, StandardCharsets.US_ASCII);
-
- int dataLength = payload.getShort() & SHORT_MASK;
- mIconData = new byte[dataLength];
- payload.get(mIconData);
- }
-
- public StatusCode getStatusCode() {
- return mStatusCode;
- }
-
- public String getType() {
- return mType;
- }
-
- public byte[] getIconData() {
- return mIconData;
- }
-
- @Override
- public String toString() {
- return "HSIconFile{" +
- "statusCode=" + mStatusCode +
- ", type='" + mType + '\'' +
- ", iconData=" + mIconData.length + " bytes }";
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/HSOsuProvidersElement.java b/service/java/com/android/server/wifi/anqp/HSOsuProvidersElement.java
deleted file mode 100644
index ee55517..0000000
--- a/service/java/com/android/server/wifi/anqp/HSOsuProvidersElement.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The OSU Providers List vendor specific ANQP Element,
- * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
- * section 4.8
- */
-public class HSOsuProvidersElement extends ANQPElement {
- private final String mSSID;
- private final List<OSUProvider> mProviders;
-
- public HSOsuProvidersElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- mSSID = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8);
- int providerCount = payload.get() & Constants.BYTE_MASK;
-
- mProviders = new ArrayList<>(providerCount);
-
- while (providerCount > 0) {
- mProviders.add(new OSUProvider(payload));
- providerCount--;
- }
- }
-
- public String getSSID() {
- return mSSID;
- }
-
- public List<OSUProvider> getProviders() {
- return Collections.unmodifiableList(mProviders);
- }
-
- @Override
- public String toString() {
- return "HSOsuProviders{" +
- "SSID='" + mSSID + '\'' +
- ", providers=" + mProviders +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/HSWanMetricsElement.java b/service/java/com/android/server/wifi/anqp/HSWanMetricsElement.java
deleted file mode 100644
index a1e1ca9..0000000
--- a/service/java/com/android/server/wifi/anqp/HSWanMetricsElement.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-import static com.android.server.wifi.anqp.Constants.INT_MASK;
-import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
-
-/**
- * The WAN Metrics vendor specific ANQP Element,
- * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
- * section 4.4
- */
-public class HSWanMetricsElement extends ANQPElement {
-
- public enum LinkStatus {Reserved, Up, Down, Test}
-
- private final LinkStatus mStatus;
- private final boolean mSymmetric;
- private final boolean mCapped;
- private final long mDlSpeed;
- private final long mUlSpeed;
- private final int mDlLoad;
- private final int mUlLoad;
- private final int mLMD;
-
- public HSWanMetricsElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- if (payload.remaining() != 13) {
- throw new ProtocolException("Bad WAN metrics length: " + payload.remaining());
- }
-
- int status = payload.get() & BYTE_MASK;
- mStatus = LinkStatus.values()[status & 0x03];
- mSymmetric = (status & 0x04) != 0;
- mCapped = (status & 0x08) != 0;
- mDlSpeed = payload.getInt() & INT_MASK;
- mUlSpeed = payload.getInt() & INT_MASK;
- mDlLoad = payload.get() & BYTE_MASK;
- mUlLoad = payload.get() & BYTE_MASK;
- mLMD = payload.getShort() & SHORT_MASK;
- }
-
- public LinkStatus getStatus() {
- return mStatus;
- }
-
- public boolean isSymmetric() {
- return mSymmetric;
- }
-
- public boolean isCapped() {
- return mCapped;
- }
-
- public long getDlSpeed() {
- return mDlSpeed;
- }
-
- public long getUlSpeed() {
- return mUlSpeed;
- }
-
- public int getDlLoad() {
- return mDlLoad;
- }
-
- public int getUlLoad() {
- return mUlLoad;
- }
-
- public int getLMD() {
- return mLMD;
- }
-
- @Override
- public String toString() {
- return String.format("HSWanMetrics{mStatus=%s, mSymmetric=%s, mCapped=%s, " +
- "mDlSpeed=%d, mUlSpeed=%d, mDlLoad=%f, mUlLoad=%f, mLMD=%d}",
- mStatus, mSymmetric, mCapped,
- mDlSpeed, mUlSpeed,
- (double)mDlLoad * 100.0 / 256.0,
- (double)mUlLoad * 100.0 / 256.0,
- mLMD);
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/I18Name.java b/service/java/com/android/server/wifi/anqp/I18Name.java
deleted file mode 100644
index 6f247aa..0000000
--- a/service/java/com/android/server/wifi/anqp/I18Name.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-
-import java.io.IOException;
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Locale;
-
-/**
- * A generic Internationalized name used in ANQP elements as specified in 802.11-2012 and
- * "Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00"
- */
-public class I18Name {
- private final String mLanguage;
- private final Locale mLocale;
- private final String mText;
-
- public I18Name(ByteBuffer payload) throws ProtocolException {
- if (payload.remaining() < Constants.LANG_CODE_LENGTH + 1) {
- throw new ProtocolException("Truncated I18Name: " + payload.remaining());
- }
- int nameLength = payload.get() & BYTE_MASK;
- if (nameLength < Constants.LANG_CODE_LENGTH) {
- throw new ProtocolException("Runt I18Name: " + nameLength);
- }
- mLanguage = Constants.getTrimmedString(payload,
- Constants.LANG_CODE_LENGTH, StandardCharsets.US_ASCII);
- mLocale = Locale.forLanguageTag(mLanguage);
- mText = Constants.getString(payload, nameLength -
- Constants.LANG_CODE_LENGTH, StandardCharsets.UTF_8);
- }
-
- public I18Name(String compoundString) throws IOException {
- if (compoundString.length() < Constants.LANG_CODE_LENGTH) {
- throw new IOException("I18String too short: '" + compoundString + "'");
- }
- mLanguage = compoundString.substring(0, Constants.LANG_CODE_LENGTH);
- mText = compoundString.substring(Constants.LANG_CODE_LENGTH);
- mLocale = Locale.forLanguageTag(mLanguage);
- }
-
- public String getLanguage() {
- return mLanguage;
- }
-
- public Locale getLocale() {
- return mLocale;
- }
-
- public String getText() {
- return mText;
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (this == thatObject) {
- return true;
- }
- if (thatObject == null || getClass() != thatObject.getClass()) {
- return false;
- }
-
- I18Name that = (I18Name) thatObject;
- return mLanguage.equals(that.mLanguage) && mText.equals(that.mText);
- }
-
- @Override
- public int hashCode() {
- int result = mLanguage.hashCode();
- result = 31 * result + mText.hashCode();
- return result;
- }
-
- @Override
- public String toString() {
- return mText + ':' + mLocale.getLanguage();
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/IPAddressTypeAvailabilityElement.java b/service/java/com/android/server/wifi/anqp/IPAddressTypeAvailabilityElement.java
deleted file mode 100644
index 8d71a3b..0000000
--- a/service/java/com/android/server/wifi/anqp/IPAddressTypeAvailabilityElement.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-
-/**
- * The IP Address Type availability ANQP Element, IEEE802.11-2012 section 8.4.4.9
- */
-public class IPAddressTypeAvailabilityElement extends ANQPElement {
- public enum IPv4Availability {
- NotAvailable, Public, PortRestricted, SingleNAT, DoubleNAT,
- PortRestrictedAndSingleNAT, PortRestrictedAndDoubleNAT, Unknown
- }
-
- public enum IPv6Availability {NotAvailable, Available, Unknown, Reserved}
-
- private final IPv4Availability mV4Availability;
- private final IPv6Availability mV6Availability;
-
- public IPAddressTypeAvailabilityElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- if (payload.remaining() != 1)
- throw new ProtocolException("Bad IP Address Type Availability length: " +
- payload.remaining());
-
- int ipField = payload.get();
- mV6Availability = IPv6Availability.values()[ipField & 0x3];
-
- ipField = (ipField >> 2) & 0x3f;
- mV4Availability = ipField < IPv4Availability.values().length ?
- IPv4Availability.values()[ipField] :
- IPv4Availability.Unknown;
- }
-
- public IPv4Availability getV4Availability() {
- return mV4Availability;
- }
-
- public IPv6Availability getV6Availability() {
- return mV6Availability;
- }
-
- @Override
- public String toString() {
- return "IPAddressTypeAvailability{" +
- "mV4Availability=" + mV4Availability +
- ", mV6Availability=" + mV6Availability +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/NAIRealmData.java b/service/java/com/android/server/wifi/anqp/NAIRealmData.java
deleted file mode 100644
index 36323f7..0000000
--- a/service/java/com/android/server/wifi/anqp/NAIRealmData.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import com.android.server.wifi.anqp.eap.EAPMethod;
-import com.android.server.wifi.hotspot2.AuthMatch;
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.hotspot2.pps.Credential;
-import com.android.server.wifi.hotspot2.pps.DomainMatcher;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * The NAI Realm Data ANQP sub-element, IEEE802.11-2012 section 8.4.4.10 figure 8-418
- */
-public class NAIRealmData {
- private final List<String> mRealms;
- private final List<EAPMethod> mEAPMethods;
-
- public NAIRealmData(ByteBuffer payload) throws ProtocolException {
- if (payload.remaining() < 5) {
- throw new ProtocolException("Runt payload: " + payload.remaining());
- }
-
- int length = payload.getShort() & Constants.SHORT_MASK;
- if (length > payload.remaining()) {
- throw new ProtocolException("Invalid data length: " + length);
- }
- boolean utf8 = (payload.get() & 1) == Constants.UTF8_INDICATOR;
-
- String realm = Constants.getPrefixedString(payload, 1, utf8 ?
- StandardCharsets.UTF_8 :
- StandardCharsets.US_ASCII);
- String[] realms = realm.split(";");
- mRealms = new ArrayList<>();
- for (String realmElement : realms) {
- if (realmElement.length() > 0) {
- mRealms.add(realmElement);
- }
- }
-
- int methodCount = payload.get() & Constants.BYTE_MASK;
- mEAPMethods = new ArrayList<>(methodCount);
- while (methodCount > 0) {
- mEAPMethods.add(new EAPMethod(payload));
- methodCount--;
- }
- }
-
- public List<String> getRealms() {
- return Collections.unmodifiableList(mRealms);
- }
-
- public List<EAPMethod> getEAPMethods() {
- return Collections.unmodifiableList(mEAPMethods);
- }
-
- public int match(List<String> credLabels, Credential credential) {
- int realmMatch = AuthMatch.None;
- if (!mRealms.isEmpty()) {
- for (String realm : mRealms) {
- List<String> labels = Utils.splitDomain(realm);
- if (DomainMatcher.arg2SubdomainOfArg1(credLabels, labels)) {
- realmMatch = AuthMatch.Realm;
- break;
- }
- }
- if (realmMatch == AuthMatch.None || mEAPMethods.isEmpty()) {
- return realmMatch;
- }
- // else there is a realm match and one or more EAP methods - check them.
- }
- else if (mEAPMethods.isEmpty()) {
- return AuthMatch.Indeterminate;
- }
-
- int best = AuthMatch.None;
- for (EAPMethod eapMethod : mEAPMethods) {
- int match = eapMethod.match(credential) | realmMatch;
- if (match > best) {
- best = match;
- if (best == AuthMatch.Exact) {
- return best;
- }
- }
- }
- return best;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
-
- sb.append(" NAI Realm(s)");
- for (String realm : mRealms) {
- sb.append(' ').append(realm);
- }
- sb.append('\n');
-
- for (EAPMethod eapMethod : mEAPMethods) {
- sb.append( " " ).append(eapMethod.toString());
- }
- return sb.toString();
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/NAIRealmElement.java b/service/java/com/android/server/wifi/anqp/NAIRealmElement.java
deleted file mode 100644
index c21dbee..0000000
--- a/service/java/com/android/server/wifi/anqp/NAIRealmElement.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import com.android.server.wifi.SIMAccessor;
-import com.android.server.wifi.hotspot2.AuthMatch;
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.hotspot2.pps.Credential;
-import com.android.server.wifi.hotspot2.pps.HomeSP;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import static com.android.server.wifi.anqp.Constants.BYTES_IN_SHORT;
-import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
-
-/**
- * The NAI Realm ANQP Element, IEEE802.11-2012 section 8.4.4.10
- */
-public class NAIRealmElement extends ANQPElement {
- private final List<NAIRealmData> mRealmData;
-
- public NAIRealmElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- if (!payload.hasRemaining()) {
- mRealmData = Collections.emptyList();
- return;
- }
-
- if (payload.remaining() < BYTES_IN_SHORT) {
- throw new ProtocolException("Runt NAI Realm: " + payload.remaining());
- }
-
- int count = payload.getShort() & SHORT_MASK;
- mRealmData = new ArrayList<>(count);
- while (count > 0) {
- mRealmData.add(new NAIRealmData(payload));
- count--;
- }
- }
-
- public List<NAIRealmData> getRealmData() {
- return Collections.unmodifiableList(mRealmData);
- }
-
- public int match(Credential credential) {
- if (mRealmData.isEmpty())
- return AuthMatch.Indeterminate;
-
- List<String> credLabels = Utils.splitDomain(credential.getRealm());
- int best = AuthMatch.None;
- for (NAIRealmData realmData : mRealmData) {
- int match = realmData.match(credLabels, credential);
- if (match > best) {
- best = match;
- if (best == AuthMatch.Exact) {
- return best;
- }
- }
- }
- return best;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("NAI Realm:\n");
- for (NAIRealmData data : mRealmData) {
- sb.append(data);
- }
- return sb.toString();
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/NetworkAuthenticationTypeElement.java b/service/java/com/android/server/wifi/anqp/NetworkAuthenticationTypeElement.java
deleted file mode 100644
index b496e3e..0000000
--- a/service/java/com/android/server/wifi/anqp/NetworkAuthenticationTypeElement.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-
-/**
- * The Network Authentication Type ANQP Element, IEEE802.11-2012 section 8.4.4.6
- */
-public class NetworkAuthenticationTypeElement extends ANQPElement {
-
- private final List<NetworkAuthentication> m_authenticationTypes;
-
- public enum NwkAuthTypeEnum {
- TermsAndConditions,
- OnLineEnrollment,
- HTTPRedirection,
- DNSRedirection,
- Reserved
- }
-
- public static class NetworkAuthentication {
- private final NwkAuthTypeEnum m_type;
- private final String m_url;
-
- private NetworkAuthentication(NwkAuthTypeEnum type, String url) {
- m_type = type;
- m_url = url;
- }
-
- public NwkAuthTypeEnum getType() {
- return m_type;
- }
-
- public String getURL() {
- return m_url;
- }
-
- @Override
- public String toString() {
- return "NetworkAuthentication{" +
- "m_type=" + m_type +
- ", m_url='" + m_url + '\'' +
- '}';
- }
- }
-
- public NetworkAuthenticationTypeElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
-
- super(infoID);
-
- m_authenticationTypes = new ArrayList<NetworkAuthentication>();
-
- while (payload.hasRemaining()) {
- int typeNumber = payload.get() & BYTE_MASK;
- NwkAuthTypeEnum type;
- type = typeNumber >= NwkAuthTypeEnum.values().length ?
- NwkAuthTypeEnum.Reserved :
- NwkAuthTypeEnum.values()[typeNumber];
-
- m_authenticationTypes.add(new NetworkAuthentication(type,
- Constants.getPrefixedString(payload, 2, StandardCharsets.UTF_8)));
- }
- }
-
- public List<NetworkAuthentication> getAuthenticationTypes() {
- return Collections.unmodifiableList(m_authenticationTypes);
- }
-
- @Override
- public String toString() {
- return "NetworkAuthenticationType{" +
- "m_authenticationTypes=" + m_authenticationTypes +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/OSUProvider.java b/service/java/com/android/server/wifi/anqp/OSUProvider.java
deleted file mode 100644
index e325ca0..0000000
--- a/service/java/com/android/server/wifi/anqp/OSUProvider.java
+++ /dev/null
@@ -1,149 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
-
-/**
- * An OSU Provider, as specified in
- * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
- * section 4.8.1
- */
-public class OSUProvider {
-
- public enum OSUMethod {OmaDm, SoapXml}
-
- private final List<I18Name> mNames;
- private final String mOSUServer;
- private final List<OSUMethod> mOSUMethods;
- private final List<IconInfo> mIcons;
- private final String mOsuNai;
- private final List<I18Name> mServiceDescriptions;
- private final int mHashCode;
-
- public OSUProvider(ByteBuffer payload) throws ProtocolException {
- if (payload.remaining() < 11) {
- throw new ProtocolException("Truncated OSU provider: " + payload.remaining());
- }
-
- int length = payload.getShort() & SHORT_MASK;
- int namesLength = payload.getShort() & SHORT_MASK;
-
- ByteBuffer namesBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
- namesBuffer.limit(namesBuffer.position() + namesLength);
- payload.position(payload.position() + namesLength);
-
- mNames = new ArrayList<>();
-
- while (namesBuffer.hasRemaining()) {
- mNames.add(new I18Name(namesBuffer));
- }
-
- mOSUServer = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8);
- int methodLength = payload.get() & BYTE_MASK;
- mOSUMethods = new ArrayList<>(methodLength);
- while (methodLength > 0) {
- int methodID = payload.get() & BYTE_MASK;
- mOSUMethods.add(methodID < OSUMethod.values().length ?
- OSUMethod.values()[methodID] :
- null);
- methodLength--;
- }
-
- int iconsLength = payload.getShort() & SHORT_MASK;
- ByteBuffer iconsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
- iconsBuffer.limit(iconsBuffer.position() + iconsLength);
- payload.position(payload.position() + iconsLength);
-
- mIcons = new ArrayList<>();
-
- while (iconsBuffer.hasRemaining()) {
- mIcons.add(new IconInfo(iconsBuffer));
- }
-
- mOsuNai = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8, true);
-
- int descriptionsLength = payload.getShort() & SHORT_MASK;
- ByteBuffer descriptionsBuffer = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
- descriptionsBuffer.limit(descriptionsBuffer.position() + descriptionsLength);
- payload.position(payload.position() + descriptionsLength);
-
- mServiceDescriptions = new ArrayList<>();
-
- while (descriptionsBuffer.hasRemaining()) {
- mServiceDescriptions.add(new I18Name(descriptionsBuffer));
- }
-
- int result = mNames.hashCode();
- result = 31 * result + mOSUServer.hashCode();
- result = 31 * result + mOSUMethods.hashCode();
- result = 31 * result + mIcons.hashCode();
- result = 31 * result + (mOsuNai != null ? mOsuNai.hashCode() : 0);
- result = 31 * result + mServiceDescriptions.hashCode();
- mHashCode = result;
- }
-
- public List<I18Name> getNames() {
- return mNames;
- }
-
- public String getOSUServer() {
- return mOSUServer;
- }
-
- public List<OSUMethod> getOSUMethods() {
- return mOSUMethods;
- }
-
- public List<IconInfo> getIcons() {
- return mIcons;
- }
-
- public String getOsuNai() {
- return mOsuNai;
- }
-
- public List<I18Name> getServiceDescriptions() {
- return mServiceDescriptions;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- OSUProvider that = (OSUProvider) o;
-
- if (!mOSUServer.equals(that.mOSUServer)) return false;
- if (!mNames.equals(that.mNames)) return false;
- if (!mServiceDescriptions.equals(that.mServiceDescriptions)) return false;
- if (!mIcons.equals(that.mIcons)) return false;
- if (!mOSUMethods.equals(that.mOSUMethods)) return false;
- if (mOsuNai != null ? !mOsuNai.equals(that.mOsuNai) : that.mOsuNai != null) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- return mHashCode;
- }
-
- @Override
- public String toString() {
- return "OSUProvider{" +
- "names=" + mNames +
- ", OSUServer='" + mOSUServer + '\'' +
- ", OSUMethods=" + mOSUMethods +
- ", icons=" + mIcons +
- ", NAI='" + mOsuNai + '\'' +
- ", serviceDescriptions=" + mServiceDescriptions +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/RawByteElement.java b/service/java/com/android/server/wifi/anqp/RawByteElement.java
deleted file mode 100644
index 42a0cdc..0000000
--- a/service/java/com/android/server/wifi/anqp/RawByteElement.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.nio.ByteBuffer;
-
-/**
- * An object holding the raw octets of an ANQP element as provided by the wpa_supplicant.
- */
-public class RawByteElement extends ANQPElement {
- private final byte[] mPayload;
-
- public RawByteElement(Constants.ANQPElementType infoID, ByteBuffer payload) {
- super(infoID);
- mPayload = new byte[payload.remaining()];
- payload.get(mPayload);
- }
-
- public byte[] getPayload() {
- return mPayload;
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/RoamingConsortiumElement.java b/service/java/com/android/server/wifi/anqp/RoamingConsortiumElement.java
deleted file mode 100644
index 80d9b72..0000000
--- a/service/java/com/android/server/wifi/anqp/RoamingConsortiumElement.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import com.android.server.wifi.hotspot2.Utils;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-import static com.android.server.wifi.anqp.Constants.getInteger;
-
-/**
- * The Roaming Consortium ANQP Element, IEEE802.11-2012 section 8.4.4.7
- */
-public class RoamingConsortiumElement extends ANQPElement {
-
- private final List<Long> mOis;
-
- public RoamingConsortiumElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- mOis = new ArrayList<Long>();
-
- while (payload.hasRemaining()) {
- int length = payload.get() & BYTE_MASK;
- if (length > payload.remaining()) {
- throw new ProtocolException("Bad OI length: " + length);
- }
- mOis.add(getInteger(payload, ByteOrder.BIG_ENDIAN, length));
- }
- }
-
- public List<Long> getOIs() {
- return Collections.unmodifiableList(mOis);
- }
-
- @Override
- public String toString() {
- return "RoamingConsortium{mOis=[" + Utils.roamingConsortiumsToString(mOis) + "]}";
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/ThreeGPPNetworkElement.java b/service/java/com/android/server/wifi/anqp/ThreeGPPNetworkElement.java
deleted file mode 100644
index 083d280..0000000
--- a/service/java/com/android/server/wifi/anqp/ThreeGPPNetworkElement.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-
-
-/**
- * The 3GPP Cellular Network ANQP Element, IEEE802.11-2012 section 8.4.4.11
- */
-public class ThreeGPPNetworkElement extends ANQPElement {
- private final int mUserData;
- private final List<CellularNetwork> mPlmns;
-
- public ThreeGPPNetworkElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- mPlmns = new ArrayList<CellularNetwork>();
- mUserData = payload.get() & BYTE_MASK;
- int length = payload.get() & BYTE_MASK;
- if (length > payload.remaining()) {
- throw new ProtocolException("Runt payload");
- }
-
- while (payload.hasRemaining()) {
- CellularNetwork network = CellularNetwork.buildCellularNetwork(payload);
- if (network != null) {
- mPlmns.add(network);
- }
- }
- }
-
- public int getUserData() {
- return mUserData;
- }
-
- public List<CellularNetwork> getPlmns() {
- return Collections.unmodifiableList(mPlmns);
- }
-
- @Override
- public String toString() {
- return "ThreeGPPNetwork{" +
- "mUserData=" + mUserData +
- ", mPlmns=" + mPlmns +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/VenueNameElement.java b/service/java/com/android/server/wifi/anqp/VenueNameElement.java
deleted file mode 100644
index f944c40..0000000
--- a/service/java/com/android/server/wifi/anqp/VenueNameElement.java
+++ /dev/null
@@ -1,192 +0,0 @@
-package com.android.server.wifi.anqp;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * The Venue Name ANQP Element, IEEE802.11-2012 section 8.4.4.4
- */
-public class VenueNameElement extends ANQPElement {
- private final VenueGroup mGroup;
- private final VenueType mType;
- private final List<I18Name> mNames;
-
- private static final Map<VenueGroup, Integer> sGroupBases =
- new EnumMap<VenueGroup, Integer>(VenueGroup.class);
-
- public VenueNameElement(Constants.ANQPElementType infoID, ByteBuffer payload)
- throws ProtocolException {
- super(infoID);
-
- if (payload.remaining() < 2)
- throw new ProtocolException("Runt Venue Name");
-
- int group = payload.get() & Constants.BYTE_MASK;
- int type = payload.get() & Constants.BYTE_MASK;
-
- if (group >= VenueGroup.Reserved.ordinal()) {
- mGroup = VenueGroup.Reserved;
- mType = VenueType.Reserved;
- } else {
- mGroup = VenueGroup.values()[group];
- type += sGroupBases.get(mGroup);
- if (type >= VenueType.Reserved.ordinal()) {
- mType = VenueType.Reserved;
- } else {
- mType = VenueType.values()[type];
- }
- }
-
- mNames = new ArrayList<I18Name>();
- while (payload.hasRemaining()) {
- mNames.add(new I18Name(payload));
- }
- }
-
- public VenueGroup getGroup() {
- return mGroup;
- }
-
- public VenueType getType() {
- return mType;
- }
-
- public List<I18Name> getNames() {
- return Collections.unmodifiableList(mNames);
- }
-
- @Override
- public String toString() {
- return "VenueName{" +
- "m_group=" + mGroup +
- ", m_type=" + mType +
- ", m_names=" + mNames +
- '}';
- }
-
- public enum VenueGroup {
- Unspecified,
- Assembly,
- Business,
- Educational,
- FactoryIndustrial,
- Institutional,
- Mercantile,
- Residential,
- Storage,
- UtilityMiscellaneous,
- Vehicular,
- Outdoor,
- Reserved // Note: this must be the last enum constant
- }
-
- public enum VenueType {
- Unspecified,
-
- UnspecifiedAssembly,
- Arena,
- Stadium,
- PassengerTerminal,
- Amphitheater,
- AmusementPark,
- PlaceOfWorship,
- ConventionCenter,
- Library,
- Museum,
- Restaurant,
- Theater,
- Bar,
- CoffeeShop,
- ZooOrAquarium,
- EmergencyCoordinationCenter,
-
- UnspecifiedBusiness,
- DoctorDentistoffice,
- Bank,
- FireStation,
- PoliceStation,
- PostOffice,
- ProfessionalOffice,
- ResearchDevelopmentFacility,
- AttorneyOffice,
-
- UnspecifiedEducational,
- SchoolPrimary,
- SchoolSecondary,
- UniversityCollege,
-
- UnspecifiedFactoryIndustrial,
- Factory,
-
- UnspecifiedInstitutional,
- Hospital,
- LongTermCareFacility,
- AlcoholAndDrugRehabilitationCenter,
- GroupHome,
- PrisonJail,
-
- UnspecifiedMercantile,
- RetailStore,
- GroceryMarket,
- AutomotiveServiceStation,
- ShoppingMall,
- GasStation,
-
- UnspecifiedResidential,
- PrivateResidence,
- HotelMotel,
- Dormitory,
- BoardingHouse,
-
- UnspecifiedStorage,
-
- UnspecifiedUtilityMiscellaneous,
-
- UnspecifiedVehicular,
- AutomobileOrTruck,
- Airplane,
- Bus,
- Ferry,
- ShipOrBoat,
- Train,
- MotorBike,
-
- UnspecifiedOutdoor,
- MuniMeshNetwork,
- CityPark,
- RestArea,
- TrafficControl,
- BusStop,
- Kiosk,
-
- Reserved // Note: this must be the last enum constant
- }
-
- private static final VenueType[] PerGroup =
- {
- VenueType.Unspecified,
- VenueType.UnspecifiedAssembly,
- VenueType.UnspecifiedBusiness,
- VenueType.UnspecifiedEducational,
- VenueType.UnspecifiedFactoryIndustrial,
- VenueType.UnspecifiedInstitutional,
- VenueType.UnspecifiedMercantile,
- VenueType.UnspecifiedResidential,
- VenueType.UnspecifiedStorage,
- VenueType.UnspecifiedUtilityMiscellaneous,
- VenueType.UnspecifiedVehicular,
- VenueType.UnspecifiedOutdoor
- };
-
- static {
- int index = 0;
- for (VenueType venue : PerGroup) {
- sGroupBases.put(VenueGroup.values()[index++], venue.ordinal());
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/eap/AuthParam.java b/service/java/com/android/server/wifi/anqp/eap/AuthParam.java
deleted file mode 100644
index f7c877a..0000000
--- a/service/java/com/android/server/wifi/anqp/eap/AuthParam.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.android.server.wifi.anqp.eap;
-
-/**
- * An Authentication parameter, part of the NAI Realm ANQP element, specified in
- * IEEE802.11-2012 section 8.4.4.10, table 8-188
- */
-public interface AuthParam {
- public EAP.AuthInfoID getAuthInfoID();
-}
diff --git a/service/java/com/android/server/wifi/anqp/eap/Credential.java b/service/java/com/android/server/wifi/anqp/eap/Credential.java
deleted file mode 100644
index d3aca07..0000000
--- a/service/java/com/android/server/wifi/anqp/eap/Credential.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.android.server.wifi.anqp.eap;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-
-/**
- * An EAP authentication parameter, IEEE802.11-2012, table 8-188
- */
-public class Credential implements AuthParam {
-
- public enum CredType {
- Reserved,
- SIM,
- USIM,
- NFC,
- HWToken,
- Softoken,
- Certificate,
- Username,
- None,
- Anonymous,
- VendorSpecific}
-
- private final EAP.AuthInfoID mAuthInfoID;
- private final CredType mCredType;
-
- public Credential(EAP.AuthInfoID infoID, int length, ByteBuffer payload)
- throws ProtocolException {
- if (length != 1) {
- throw new ProtocolException("Bad length: " + length);
- }
-
- mAuthInfoID = infoID;
- int typeID = payload.get() & BYTE_MASK;
-
- mCredType = typeID < CredType.values().length ?
- CredType.values()[typeID] :
- CredType.Reserved;
- }
-
- @Override
- public EAP.AuthInfoID getAuthInfoID() {
- return mAuthInfoID;
- }
-
- @Override
- public int hashCode() {
- return mAuthInfoID.hashCode() * 31 + mCredType.hashCode();
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (thatObject == this) {
- return true;
- } else if (thatObject == null || thatObject.getClass() != Credential.class) {
- return false;
- } else {
- return ((Credential) thatObject).getCredType() == getCredType();
- }
- }
-
- public CredType getCredType() {
- return mCredType;
- }
-
- @Override
- public String toString() {
- return "Auth method " + mAuthInfoID + " = " + mCredType + "\n";
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/eap/EAP.java b/service/java/com/android/server/wifi/anqp/eap/EAP.java
deleted file mode 100644
index bdb88e4..0000000
--- a/service/java/com/android/server/wifi/anqp/eap/EAP.java
+++ /dev/null
@@ -1,155 +0,0 @@
-package com.android.server.wifi.anqp.eap;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * EAP Related constants for the ANQP NAIRealm element, IEEE802.11-2012 section 8.4.4.10
- */
-public abstract class EAP {
-
- private static final Map<Integer, EAPMethodID> sEapIds = new HashMap<>();
- private static final Map<EAPMethodID, Integer> sRevEapIds = new HashMap<>();
- private static final Map<Integer, AuthInfoID> sAuthIds = new HashMap<>();
-
- public static final int EAP_MD5 = 4;
- public static final int EAP_OTP = 5;
- public static final int EAP_RSA = 9;
- public static final int EAP_KEA = 11;
- public static final int EAP_KEA_VALIDATE = 12;
- public static final int EAP_TLS = 13;
- public static final int EAP_LEAP = 17;
- public static final int EAP_SIM = 18;
- public static final int EAP_TTLS = 21;
- public static final int EAP_AKA = 23;
- public static final int EAP_3Com = 24;
- public static final int EAP_MSCHAPv2 = 26;
- public static final int EAP_PEAP = 29;
- public static final int EAP_POTP = 32;
- public static final int EAP_ActiontecWireless = 35;
- public static final int EAP_HTTPDigest = 38;
- public static final int EAP_SPEKE = 41;
- public static final int EAP_MOBAC = 42;
- public static final int EAP_FAST = 43;
- public static final int EAP_ZLXEAP = 44;
- public static final int EAP_Link = 45;
- public static final int EAP_PAX = 46;
- public static final int EAP_PSK = 47;
- public static final int EAP_SAKE = 48;
- public static final int EAP_IKEv2 = 49;
- public static final int EAP_AKAPrim = 50;
- public static final int EAP_GPSK = 51;
- public static final int EAP_PWD = 52;
- public static final int EAP_EKE = 53;
- public static final int EAP_TEAP = 55;
-
- public enum EAPMethodID {
- EAP_MD5,
- EAP_OTP,
- EAP_RSA,
- EAP_KEA,
- EAP_KEA_VALIDATE,
- EAP_TLS,
- EAP_LEAP,
- EAP_SIM,
- EAP_TTLS,
- EAP_AKA,
- EAP_3Com,
- EAP_MSCHAPv2,
- EAP_PEAP,
- EAP_POTP,
- EAP_ActiontecWireless,
- EAP_HTTPDigest,
- EAP_SPEKE,
- EAP_MOBAC,
- EAP_FAST,
- EAP_ZLXEAP,
- EAP_Link,
- EAP_PAX,
- EAP_PSK,
- EAP_SAKE,
- EAP_IKEv2,
- EAP_AKAPrim,
- EAP_GPSK,
- EAP_PWD,
- EAP_EKE,
- EAP_TEAP
- }
-
- public static final int ExpandedEAPMethod = 1;
- public static final int NonEAPInnerAuthType = 2;
- public static final int InnerAuthEAPMethodType = 3;
- public static final int ExpandedInnerEAPMethod = 4;
- public static final int CredentialType = 5;
- public static final int TunneledEAPMethodCredType = 6;
- public static final int VendorSpecific = 221;
-
- public enum AuthInfoID {
- Undefined,
- ExpandedEAPMethod,
- NonEAPInnerAuthType,
- InnerAuthEAPMethodType,
- ExpandedInnerEAPMethod,
- CredentialType,
- TunneledEAPMethodCredType,
- VendorSpecific
- }
-
- static {
- sEapIds.put(EAP_MD5, EAPMethodID.EAP_MD5);
- sEapIds.put(EAP_OTP, EAPMethodID.EAP_OTP);
- sEapIds.put(EAP_RSA, EAPMethodID.EAP_RSA);
- sEapIds.put(EAP_KEA, EAPMethodID.EAP_KEA);
- sEapIds.put(EAP_KEA_VALIDATE, EAPMethodID.EAP_KEA_VALIDATE);
- sEapIds.put(EAP_TLS, EAPMethodID.EAP_TLS);
- sEapIds.put(EAP_LEAP, EAPMethodID.EAP_LEAP);
- sEapIds.put(EAP_SIM, EAPMethodID.EAP_SIM);
- sEapIds.put(EAP_TTLS, EAPMethodID.EAP_TTLS);
- sEapIds.put(EAP_AKA, EAPMethodID.EAP_AKA);
- sEapIds.put(EAP_3Com, EAPMethodID.EAP_3Com);
- sEapIds.put(EAP_MSCHAPv2, EAPMethodID.EAP_MSCHAPv2);
- sEapIds.put(EAP_PEAP, EAPMethodID.EAP_PEAP);
- sEapIds.put(EAP_POTP, EAPMethodID.EAP_POTP);
- sEapIds.put(EAP_ActiontecWireless, EAPMethodID.EAP_ActiontecWireless);
- sEapIds.put(EAP_HTTPDigest, EAPMethodID.EAP_HTTPDigest);
- sEapIds.put(EAP_SPEKE, EAPMethodID.EAP_SPEKE);
- sEapIds.put(EAP_MOBAC, EAPMethodID.EAP_MOBAC);
- sEapIds.put(EAP_FAST, EAPMethodID.EAP_FAST);
- sEapIds.put(EAP_ZLXEAP, EAPMethodID.EAP_ZLXEAP);
- sEapIds.put(EAP_Link, EAPMethodID.EAP_Link);
- sEapIds.put(EAP_PAX, EAPMethodID.EAP_PAX);
- sEapIds.put(EAP_PSK, EAPMethodID.EAP_PSK);
- sEapIds.put(EAP_SAKE, EAPMethodID.EAP_SAKE);
- sEapIds.put(EAP_IKEv2, EAPMethodID.EAP_IKEv2);
- sEapIds.put(EAP_AKAPrim, EAPMethodID.EAP_AKAPrim);
- sEapIds.put(EAP_GPSK, EAPMethodID.EAP_GPSK);
- sEapIds.put(EAP_PWD, EAPMethodID.EAP_PWD);
- sEapIds.put(EAP_EKE, EAPMethodID.EAP_EKE);
- sEapIds.put(EAP_TEAP, EAPMethodID.EAP_TEAP);
-
- for (Map.Entry<Integer, EAPMethodID> entry : sEapIds.entrySet()) {
- sRevEapIds.put(entry.getValue(), entry.getKey());
- }
-
- sAuthIds.put(ExpandedEAPMethod, AuthInfoID.ExpandedEAPMethod);
- sAuthIds.put(NonEAPInnerAuthType, AuthInfoID.NonEAPInnerAuthType);
- sAuthIds.put(InnerAuthEAPMethodType, AuthInfoID.InnerAuthEAPMethodType);
- sAuthIds.put(ExpandedInnerEAPMethod, AuthInfoID.ExpandedInnerEAPMethod);
- sAuthIds.put(CredentialType, AuthInfoID.CredentialType);
- sAuthIds.put(TunneledEAPMethodCredType, AuthInfoID.TunneledEAPMethodCredType);
- sAuthIds.put(VendorSpecific, AuthInfoID.VendorSpecific);
- }
-
- public static EAPMethodID mapEAPMethod(int methodID) {
- return sEapIds.get(methodID);
- }
-
- public static Integer mapEAPMethod(EAPMethodID methodID) {
- return sRevEapIds.get(methodID);
- }
-
- public static AuthInfoID mapAuthMethod(int methodID) {
- return sAuthIds.get(methodID);
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/eap/EAPMethod.java b/service/java/com/android/server/wifi/anqp/eap/EAPMethod.java
deleted file mode 100644
index 97c53a4..0000000
--- a/service/java/com/android/server/wifi/anqp/eap/EAPMethod.java
+++ /dev/null
@@ -1,192 +0,0 @@
-package com.android.server.wifi.anqp.eap;
-
-
-import com.android.server.wifi.anqp.Constants;
-import com.android.server.wifi.hotspot2.AuthMatch;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * An EAP Method, part of the NAI Realm ANQP element, specified in
- * IEEE802.11-2012 section 8.4.4.10, figure 8-420
- */
-public class EAPMethod {
- private final EAP.EAPMethodID mEAPMethodID;
- private final Map<EAP.AuthInfoID, Set<AuthParam>> mAuthParams;
-
- public EAPMethod(ByteBuffer payload) throws ProtocolException {
- if (payload.remaining() < 3) {
- throw new ProtocolException("Runt EAP Method: " + payload.remaining());
- }
-
- int length = payload.get() & Constants.BYTE_MASK;
- int methodID = payload.get() & Constants.BYTE_MASK;
- int count = payload.get() & Constants.BYTE_MASK;
-
- mEAPMethodID = EAP.mapEAPMethod(methodID);
- mAuthParams = new EnumMap<>(EAP.AuthInfoID.class);
-
- int realCount = 0;
-
- ByteBuffer paramPayload = payload.duplicate().order(ByteOrder.LITTLE_ENDIAN);
- paramPayload.limit(paramPayload.position() + length - 2);
- payload.position(payload.position() + length - 2);
- while (paramPayload.hasRemaining()) {
- int id = paramPayload.get() & Constants.BYTE_MASK;
-
- EAP.AuthInfoID authInfoID = EAP.mapAuthMethod(id);
- if (authInfoID == null) {
- throw new ProtocolException("Unknown auth parameter ID: " + id);
- }
-
- int len = paramPayload.get() & Constants.BYTE_MASK;
- if (len == 0 || len > paramPayload.remaining()) {
- throw new ProtocolException("Bad auth method length: " + len);
- }
-
- switch (authInfoID) {
- case ExpandedEAPMethod:
- addAuthParam(new ExpandedEAPMethod(authInfoID, len, paramPayload));
- break;
- case NonEAPInnerAuthType:
- addAuthParam(new NonEAPInnerAuth(len, paramPayload));
- break;
- case InnerAuthEAPMethodType:
- addAuthParam(new InnerAuthEAP(len, paramPayload));
- break;
- case ExpandedInnerEAPMethod:
- addAuthParam(new ExpandedEAPMethod(authInfoID, len, paramPayload));
- break;
- case CredentialType:
- addAuthParam(new Credential(authInfoID, len, paramPayload));
- break;
- case TunneledEAPMethodCredType:
- addAuthParam(new Credential(authInfoID, len, paramPayload));
- break;
- case VendorSpecific:
- addAuthParam(new VendorSpecificAuth(len, paramPayload));
- break;
- }
-
- realCount++;
- }
- if (realCount != count)
- throw new ProtocolException("Invalid parameter count: " + realCount +
- ", expected " + count);
- }
-
- public EAPMethod(EAP.EAPMethodID eapMethodID, AuthParam authParam) {
- mEAPMethodID = eapMethodID;
- mAuthParams = new HashMap<>(1);
- if (authParam != null) {
- Set<AuthParam> authParams = new HashSet<>();
- authParams.add(authParam);
- mAuthParams.put(authParam.getAuthInfoID(), authParams);
- }
- }
-
- private void addAuthParam(AuthParam param) {
- Set<AuthParam> authParams = mAuthParams.get(param.getAuthInfoID());
- if (authParams == null) {
- authParams = new HashSet<>();
- mAuthParams.put(param.getAuthInfoID(), authParams);
- }
- authParams.add(param);
- }
-
- public Map<EAP.AuthInfoID, Set<AuthParam>> getAuthParams() {
- return Collections.unmodifiableMap(mAuthParams);
- }
-
- public EAP.EAPMethodID getEAPMethodID() {
- return mEAPMethodID;
- }
-
- public int match(com.android.server.wifi.hotspot2.pps.Credential credential) {
-
- EAPMethod credMethod = credential.getEAPMethod();
- if (mEAPMethodID != credMethod.getEAPMethodID()) {
- return AuthMatch.None;
- }
-
- switch (mEAPMethodID) {
- case EAP_TTLS:
- if (mAuthParams.isEmpty()) {
- return AuthMatch.Method;
- }
- int paramCount = 0;
- for (Map.Entry<EAP.AuthInfoID, Set<AuthParam>> entry :
- credMethod.getAuthParams().entrySet()) {
- Set<AuthParam> params = mAuthParams.get(entry.getKey());
- if (params == null) {
- continue;
- }
-
- if (!Collections.disjoint(params, entry.getValue())) {
- return AuthMatch.MethodParam;
- }
- paramCount += params.size();
- }
- return paramCount > 0 ? AuthMatch.None : AuthMatch.Method;
- case EAP_TLS:
- return AuthMatch.MethodParam;
- case EAP_SIM:
- case EAP_AKA:
- case EAP_AKAPrim:
- return AuthMatch.Method;
- default:
- return AuthMatch.Method;
- }
- }
-
- public AuthParam getAuthParam() {
- if (mAuthParams.isEmpty()) {
- return null;
- }
- Set<AuthParam> params = mAuthParams.values().iterator().next();
- if (params.isEmpty()) {
- return null;
- }
- return params.iterator().next();
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (this == thatObject) {
- return true;
- }
- else if (thatObject == null || getClass() != thatObject.getClass()) {
- return false;
- }
-
- EAPMethod that = (EAPMethod) thatObject;
- return mEAPMethodID == that.mEAPMethodID && mAuthParams.equals(that.mAuthParams);
- }
-
- @Override
- public int hashCode() {
- int result = mEAPMethodID.hashCode();
- result = 31 * result + mAuthParams.hashCode();
- return result;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("EAP Method ").append(mEAPMethodID).append('\n');
- for (Set<AuthParam> paramSet : mAuthParams.values()) {
- for (AuthParam param : paramSet) {
- sb.append(" ").append(param.toString());
- }
- }
- return sb.toString();
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/eap/ExpandedEAPMethod.java b/service/java/com/android/server/wifi/anqp/eap/ExpandedEAPMethod.java
deleted file mode 100644
index 95763a4..0000000
--- a/service/java/com/android/server/wifi/anqp/eap/ExpandedEAPMethod.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package com.android.server.wifi.anqp.eap;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-import static com.android.server.wifi.anqp.Constants.INT_MASK;
-import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
-
-/**
- * An EAP authentication parameter, IEEE802.11-2012, table 8-188
- */
-public class ExpandedEAPMethod implements AuthParam {
-
- private final EAP.AuthInfoID mAuthInfoID;
- private final int mVendorID;
- private final long mVendorType;
-
- public ExpandedEAPMethod(EAP.AuthInfoID authInfoID, int length, ByteBuffer payload)
- throws ProtocolException {
- if (length != 7) {
- throw new ProtocolException("Bad length: " + payload.remaining());
- }
-
- mAuthInfoID = authInfoID;
-
- ByteBuffer vndBuffer = payload.duplicate().order(ByteOrder.BIG_ENDIAN);
-
- int id = vndBuffer.getShort() & SHORT_MASK;
- id = (id << Byte.SIZE) | (vndBuffer.get() & BYTE_MASK);
- mVendorID = id;
- mVendorType = vndBuffer.getInt() & INT_MASK;
-
- payload.position(payload.position()+7);
- }
-
- public ExpandedEAPMethod(EAP.AuthInfoID authInfoID, int vendorID, long vendorType) {
- mAuthInfoID = authInfoID;
- mVendorID = vendorID;
- mVendorType = vendorType;
- }
-
- @Override
- public EAP.AuthInfoID getAuthInfoID() {
- return mAuthInfoID;
- }
-
- @Override
- public int hashCode() {
- return (mAuthInfoID.hashCode() * 31 + mVendorID) * 31 + (int) mVendorType;
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (thatObject == this) {
- return true;
- } else if (thatObject == null || thatObject.getClass() != ExpandedEAPMethod.class) {
- return false;
- } else {
- ExpandedEAPMethod that = (ExpandedEAPMethod) thatObject;
- return that.getVendorID() == getVendorID() && that.getVendorType() == getVendorType();
- }
- }
-
- public int getVendorID() {
- return mVendorID;
- }
-
- public long getVendorType() {
- return mVendorType;
- }
-
- @Override
- public String toString() {
- return "Auth method " + mAuthInfoID + ", id " + mVendorID + ", type " + mVendorType + "\n";
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/eap/InnerAuthEAP.java b/service/java/com/android/server/wifi/anqp/eap/InnerAuthEAP.java
deleted file mode 100644
index a5bc4f1..0000000
--- a/service/java/com/android/server/wifi/anqp/eap/InnerAuthEAP.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.android.server.wifi.anqp.eap;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-
-/**
- * An EAP authentication parameter, IEEE802.11-2012, table 8-188
- */
-public class InnerAuthEAP implements AuthParam {
-
- private final EAP.EAPMethodID mEapMethodID;
-
- public InnerAuthEAP(int length, ByteBuffer payload) throws ProtocolException {
- if (length != 1) {
- throw new ProtocolException("Bad length: " + length);
- }
- int typeID = payload.get() & BYTE_MASK;
- mEapMethodID = EAP.mapEAPMethod(typeID);
- }
-
- public InnerAuthEAP(EAP.EAPMethodID eapMethodID) {
- mEapMethodID = eapMethodID;
- }
-
- @Override
- public EAP.AuthInfoID getAuthInfoID() {
- return EAP.AuthInfoID.InnerAuthEAPMethodType;
- }
-
- public EAP.EAPMethodID getEAPMethodID() {
- return mEapMethodID;
- }
-
- @Override
- public int hashCode() {
- return mEapMethodID != null ? mEapMethodID.hashCode() : 0;
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (thatObject == this) {
- return true;
- } else if (thatObject == null || thatObject.getClass() != InnerAuthEAP.class) {
- return false;
- } else {
- return ((InnerAuthEAP) thatObject).getEAPMethodID() == getEAPMethodID();
- }
- }
-
- @Override
- public String toString() {
- return "Auth method InnerAuthEAP, inner = " + mEapMethodID + '\n';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/eap/NonEAPInnerAuth.java b/service/java/com/android/server/wifi/anqp/eap/NonEAPInnerAuth.java
deleted file mode 100644
index a96124a..0000000
--- a/service/java/com/android/server/wifi/anqp/eap/NonEAPInnerAuth.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package com.android.server.wifi.anqp.eap;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.Map;
-
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-
-/**
- * An EAP authentication parameter, IEEE802.11-2012, table 8-188
- */
-public class NonEAPInnerAuth implements AuthParam {
-
- public enum NonEAPType {Reserved, PAP, CHAP, MSCHAP, MSCHAPv2}
- private static final Map<NonEAPType, String> sOmaMap = new EnumMap<>(NonEAPType.class);
- private static final Map<String, NonEAPType> sRevOmaMap = new HashMap<>();
-
- private final NonEAPType mType;
-
- static {
- sOmaMap.put(NonEAPType.PAP, "PAP");
- sOmaMap.put(NonEAPType.CHAP, "CHAP");
- sOmaMap.put(NonEAPType.MSCHAP, "MS-CHAP");
- sOmaMap.put(NonEAPType.MSCHAPv2, "MS-CHAP-V2");
-
- for (Map.Entry<NonEAPType, String> entry : sOmaMap.entrySet()) {
- sRevOmaMap.put(entry.getValue(), entry.getKey());
- }
- }
-
- public NonEAPInnerAuth(int length, ByteBuffer payload) throws ProtocolException {
- if (length != 1) {
- throw new ProtocolException("Bad length: " + payload.remaining());
- }
-
- int typeID = payload.get() & BYTE_MASK;
- mType = typeID < NonEAPType.values().length ?
- NonEAPType.values()[typeID] :
- NonEAPType.Reserved;
- }
-
- public NonEAPInnerAuth(NonEAPType type) {
- mType = type;
- }
-
- /**
- * Construct from the OMA-DM PPS data
- * @param eapType as defined in the HS2.0 spec.
- */
- public NonEAPInnerAuth(String eapType) {
- mType = sRevOmaMap.get(eapType);
- }
-
- @Override
- public EAP.AuthInfoID getAuthInfoID() {
- return EAP.AuthInfoID.NonEAPInnerAuthType;
- }
-
- public NonEAPType getType() {
- return mType;
- }
-
- public String getOMAtype() {
- return sOmaMap.get(mType);
- }
-
- public static String mapInnerType(NonEAPType type) {
- return sOmaMap.get(type);
- }
-
- @Override
- public int hashCode() {
- return mType.hashCode();
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (thatObject == this) {
- return true;
- } else if (thatObject == null || thatObject.getClass() != NonEAPInnerAuth.class) {
- return false;
- } else {
- return ((NonEAPInnerAuth) thatObject).getType() == getType();
- }
- }
-
- @Override
- public String toString() {
- return "Auth method NonEAPInnerAuthEAP, inner = " + mType + '\n';
- }
-}
diff --git a/service/java/com/android/server/wifi/anqp/eap/VendorSpecificAuth.java b/service/java/com/android/server/wifi/anqp/eap/VendorSpecificAuth.java
deleted file mode 100644
index 1d94192..0000000
--- a/service/java/com/android/server/wifi/anqp/eap/VendorSpecificAuth.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.android.server.wifi.anqp.eap;
-
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-/**
- * An EAP authentication parameter, IEEE802.11-2012, table 8-188
- */
-public class VendorSpecificAuth implements AuthParam {
-
- private final byte[] mData;
-
- public VendorSpecificAuth(int length, ByteBuffer payload) throws ProtocolException {
- mData = new byte[length];
- payload.get(mData);
- }
-
- @Override
- public EAP.AuthInfoID getAuthInfoID() {
- return EAP.AuthInfoID.VendorSpecific;
- }
-
- public int hashCode() {
- return Arrays.hashCode(mData);
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (thatObject == this) {
- return true;
- } else if (thatObject == null || thatObject.getClass() != VendorSpecificAuth.class) {
- return false;
- } else {
- return Arrays.equals(((VendorSpecificAuth) thatObject).getData(), getData());
- }
- }
-
- public byte[] getData() {
- return mData;
- }
-
- @Override
- public String toString() {
- return "Auth method VendorSpecificAuth, data = " + Arrays.toString(mData) + '\n';
- }
-}
diff --git a/service/java/com/android/server/wifi/aware/Capabilities.java b/service/java/com/android/server/wifi/aware/Capabilities.java
new file mode 100644
index 0000000..63076a8
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/Capabilities.java
@@ -0,0 +1,70 @@
+/*
+ * 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.aware;
+
+import android.net.wifi.aware.Characteristics;
+import android.os.Bundle;
+
+/**
+ * A container class for Aware (vendor) implementation capabilities (or
+ * limitations). Filled-in by the firmware.
+ */
+public class Capabilities {
+ public int maxConcurrentAwareClusters;
+ public int maxPublishes;
+ public int maxSubscribes;
+ public int maxServiceNameLen;
+ public int maxMatchFilterLen;
+ public int maxTotalMatchFilterLen;
+ public int maxServiceSpecificInfoLen;
+ public int maxExtendedServiceSpecificInfoLen;
+ public int maxNdiInterfaces;
+ public int maxNdpSessions;
+ public int maxAppInfoLen;
+ public int maxQueuedTransmitMessages;
+ public int maxSubscribeInterfaceAddresses;
+ public int supportedCipherSuites;
+
+ /**
+ * Converts the internal capabilities to a parcelable & potentially app-facing
+ * characteristics bundle. Only some of the information is exposed.
+ */
+ public Characteristics toPublicCharacteristics() {
+ Bundle bundle = new Bundle();
+ bundle.putInt(Characteristics.KEY_MAX_SERVICE_NAME_LENGTH, maxServiceNameLen);
+ bundle.putInt(Characteristics.KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH,
+ maxServiceSpecificInfoLen);
+ bundle.putInt(Characteristics.KEY_MAX_MATCH_FILTER_LENGTH, maxMatchFilterLen);
+ return new Characteristics(bundle);
+ }
+
+ @Override
+ public String toString() {
+ return "Capabilities [maxConcurrentAwareClusters=" + maxConcurrentAwareClusters
+ + ", maxPublishes=" + maxPublishes + ", maxSubscribes=" + maxSubscribes
+ + ", maxServiceNameLen=" + maxServiceNameLen + ", maxMatchFilterLen="
+ + maxMatchFilterLen + ", maxTotalMatchFilterLen=" + maxTotalMatchFilterLen
+ + ", maxServiceSpecificInfoLen=" + maxServiceSpecificInfoLen
+ + ", maxExtendedServiceSpecificInfoLen=" + maxExtendedServiceSpecificInfoLen
+ + ", maxNdiInterfaces=" + maxNdiInterfaces + ", maxNdpSessions="
+ + maxNdpSessions + ", maxAppInfoLen=" + maxAppInfoLen
+ + ", maxQueuedTransmitMessages=" + maxQueuedTransmitMessages
+ + ", maxSubscribeInterfaceAddresses=" + maxSubscribeInterfaceAddresses
+ + ", supportedCipherSuites=" + supportedCipherSuites
+ + "]";
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/OWNERS b/service/java/com/android/server/wifi/aware/OWNERS
new file mode 100644
index 0000000..cf116f8
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/OWNERS
@@ -0,0 +1 @@
+etancohen@google.com
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareClientState.java b/service/java/com/android/server/wifi/aware/WifiAwareClientState.java
new file mode 100644
index 0000000..7123d01
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareClientState.java
@@ -0,0 +1,315 @@
+/*
+ * 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.aware;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.wifi.RttManager;
+import android.net.wifi.aware.ConfigRequest;
+import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import libcore.util.HexEncoding;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Manages the service-side Aware state of an individual "client". A client
+ * corresponds to a single instantiation of the WifiAwareManager - 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 Aware-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 WifiAwareClientState {
+ private static final String TAG = "WifiAwareClientState";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ /* package */ static final int CLUSTER_CHANGE_EVENT_STARTED = 0;
+ /* package */ static final int CLUSTER_CHANGE_EVENT_JOINED = 1;
+
+ private final Context mContext;
+ private final IWifiAwareEventCallback mCallback;
+ private final SparseArray<WifiAwareDiscoverySessionState> mSessions = new SparseArray<>();
+
+ private final int mClientId;
+ private ConfigRequest mConfigRequest;
+ private final int mUid;
+ private final int mPid;
+ private final String mCallingPackage;
+ private final boolean mNotifyIdentityChange;
+
+ private AppOpsManager mAppOps;
+
+ private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
+ private byte[] mLastDiscoveryInterfaceMac = ALL_ZERO_MAC;
+
+ public WifiAwareClientState(Context context, int clientId, int uid, int pid,
+ String callingPackage, IWifiAwareEventCallback callback, ConfigRequest configRequest,
+ boolean notifyIdentityChange) {
+ mContext = context;
+ mClientId = clientId;
+ mUid = uid;
+ mPid = pid;
+ mCallingPackage = callingPackage;
+ mCallback = callback;
+ mConfigRequest = configRequest;
+ mNotifyIdentityChange = notifyIdentityChange;
+
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ }
+
+ /**
+ * Destroy the current client - corresponds to a disconnect() request from
+ * the client. Destroys all discovery sessions belonging to this client.
+ */
+ public void destroy() {
+ for (int i = 0; i < mSessions.size(); ++i) {
+ mSessions.valueAt(i).terminate();
+ }
+ mSessions.clear();
+ mConfigRequest = null;
+ }
+
+ public ConfigRequest getConfigRequest() {
+ return mConfigRequest;
+ }
+
+ public int getClientId() {
+ return mClientId;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public boolean getNotifyIdentityChange() {
+ return mNotifyIdentityChange;
+ }
+
+ /**
+ * 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 Aware session corresponding to the requested ID.
+ */
+ public WifiAwareDiscoverySessionState getAwareSessionStateForPubSubId(int pubSubId) {
+ for (int i = 0; i < mSessions.size(); ++i) {
+ WifiAwareDiscoverySessionState session = mSessions.valueAt(i);
+ if (session.isPubSubIdSession(pubSubId)) {
+ return session;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Add the session to the client database.
+ *
+ * @param session Session to be added.
+ */
+ public void addSession(WifiAwareDiscoverySessionState session) {
+ int sessionId = session.getSessionId();
+ if (mSessions.get(sessionId) != null) {
+ Log.w(TAG, "createSession: sessionId already exists (replaced) - " + sessionId);
+ }
+
+ mSessions.put(sessionId, session);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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) {
+ WifiAwareDiscoverySessionState 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 WifiAwareDiscoverySessionState getSession(int sessionId) {
+ return mSessions.get(sessionId);
+ }
+
+ /**
+ * Called to dispatch the Aware 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 (VDBG) {
+ Log.v(TAG,
+ "onInterfaceAddressChange: mClientId=" + mClientId + ", mNotifyIdentityChange="
+ + mNotifyIdentityChange + ", mac=" + String.valueOf(
+ HexEncoding.encode(mac)) + ", mLastDiscoveryInterfaceMac="
+ + String.valueOf(HexEncoding.encode(mLastDiscoveryInterfaceMac)));
+ }
+ if (mNotifyIdentityChange && !Arrays.equals(mac, mLastDiscoveryInterfaceMac)) {
+ try {
+ boolean hasPermission = hasLocationingPermission();
+ if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission);
+ mCallback.onIdentityChanged(hasPermission ? mac : ALL_ZERO_MAC);
+ } catch (RemoteException e) {
+ Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
+ }
+ }
+
+ mLastDiscoveryInterfaceMac = mac;
+ }
+
+ /**
+ * Called to dispatch the Aware 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 (VDBG) {
+ Log.v(TAG,
+ "onClusterChange: mClientId=" + mClientId + ", mNotifyIdentityChange="
+ + mNotifyIdentityChange + ", mac=" + String.valueOf(
+ HexEncoding.encode(mac)) + ", currentDiscoveryInterfaceMac="
+ + String.valueOf(HexEncoding.encode(currentDiscoveryInterfaceMac))
+ + ", mLastDiscoveryInterfaceMac=" + String.valueOf(
+ HexEncoding.encode(mLastDiscoveryInterfaceMac)));
+ }
+ if (mNotifyIdentityChange && !Arrays.equals(currentDiscoveryInterfaceMac,
+ mLastDiscoveryInterfaceMac)) {
+ try {
+ boolean hasPermission = hasLocationingPermission();
+ if (VDBG) Log.v(TAG, "hasPermission=" + hasPermission);
+ mCallback.onIdentityChanged(hasPermission ? mac : ALL_ZERO_MAC);
+ } catch (RemoteException e) {
+ Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
+ }
+ }
+
+ mLastDiscoveryInterfaceMac = currentDiscoveryInterfaceMac;
+ }
+
+ private boolean hasLocationingPermission() {
+ // FINE provides COARSE, so only have to check for the latter
+ return mContext.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, mPid, mUid)
+ == PackageManager.PERMISSION_GRANTED && mAppOps.noteOp(
+ AppOpsManager.OP_COARSE_LOCATION, mUid, mCallingPackage)
+ == AppOpsManager.MODE_ALLOWED;
+ }
+
+ /**
+ * 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("AwareClientState:");
+ pw.println(" mClientId: " + mClientId);
+ pw.println(" mConfigRequest: " + mConfigRequest);
+ pw.println(" mNotifyIdentityChange: " + mNotifyIdentityChange);
+ 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/aware/WifiAwareDataPathStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
new file mode 100644
index 0000000..14d855e
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDataPathStateManager.java
@@ -0,0 +1,1003 @@
+/*
+ * 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.aware;
+
+import android.content.Context;
+import android.hardware.wifi.V1_0.NanDataPathChannelCfg;
+import android.net.ConnectivityManager;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.MatchAllNetworkSpecifier;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
+import android.net.NetworkFactory;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.net.RouteInfo;
+import android.net.wifi.aware.WifiAwareManager;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.util.HexEncoding;
+
+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.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages Aware data-path lifetime: interface creation/deletion, data-path setup and tear-down.
+ * The Aware network configuration is:
+ * - transport = TRANSPORT_WIFI_AWARE
+ * - capabilities = NET_CAPABILITY_NOT_VPN
+ * - network specifier generated by DiscoverySession.createNetworkSpecifier(...) or
+ * WifiAwareManager.createNetworkSpecifier(...).
+ */
+public class WifiAwareDataPathStateManager {
+ private static final String TAG = "WifiAwareDataPathStMgr";
+
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ private static final String AWARE_INTERFACE_PREFIX = "aware_data";
+ private static final String NETWORK_TAG = "WIFI_AWARE_FACTORY";
+ private static final String AGENT_TAG_PREFIX = "WIFI_AWARE_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 WifiAwareStateManager mMgr;
+ private final NetworkInterfaceWrapper mNiWrapper = new NetworkInterfaceWrapper();
+ private final NetworkCapabilities mNetworkCapabilitiesFilter = new NetworkCapabilities();
+ private final Set<String> mInterfaces = new HashSet<>();
+ private final Map<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation>
+ mNetworkRequestsCache = new ArrayMap<>();
+ private Context mContext;
+ private Looper mLooper;
+ private WifiAwareNetworkFactory mNetworkFactory;
+ private INetworkManagementService mNwService;
+
+ public WifiAwareDataPathStateManager(WifiAwareStateManager mgr) {
+ mMgr = mgr;
+ }
+
+ /**
+ * Initialize the Aware 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_AWARE);
+ 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(new MatchAllNetworkSpecifier());
+ mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL);
+ mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(NETWORK_FACTORY_BANDWIDTH_AVAIL);
+ mNetworkCapabilitiesFilter.setSignalStrength(NETWORK_FACTORY_SIGNAL_STRENGTH_AVAIL);
+
+ mNetworkFactory = new WifiAwareNetworkFactory(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<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation>
+ getNetworkRequestByNdpId(int ndpId) {
+ for (Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> entry :
+ mNetworkRequestsCache.entrySet()) {
+ if (entry.getValue().ndpId == ndpId) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Create all Aware 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.getCapabilities() == null) {
+ Log.e(TAG, "createAllInterfaces: capabilities aren't initialized yet!");
+ return;
+ }
+
+ for (int i = 0; i < mMgr.getCapabilities().maxNdiInterfaces; ++i) {
+ String name = AWARE_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 Aware 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(WifiAwareNetworkSpecifier networkSpecifier, int ndpId) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onDataPathInitiateSuccess: networkSpecifier=" + networkSpecifier + ", ndpId="
+ + ndpId);
+ }
+
+ AwareNetworkRequestInformation 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
+ != AwareNetworkRequestInformation.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 = AwareNetworkRequestInformation.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(WifiAwareNetworkSpecifier networkSpecifier, int reason) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onDataPathInitiateFail: networkSpecifier=" + networkSpecifier + ", reason="
+ + reason);
+ }
+
+ AwareNetworkRequestInformation nnri = mNetworkRequestsCache.remove(networkSpecifier);
+ if (nnri == null) {
+ Log.w(TAG, "onDataPathInitiateFail: network request not found for networkSpecifier="
+ + networkSpecifier);
+ return;
+ }
+
+ if (nnri.state
+ != AwareNetworkRequestInformation.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.
+ * @return The network specifier of the data-path (or null if none/error)
+ */
+ public WifiAwareNetworkSpecifier onDataPathRequest(int pubSubId, byte[] mac, int ndpId) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onDataPathRequest: pubSubId=" + pubSubId + ", mac=" + String.valueOf(
+ HexEncoding.encode(mac)) + ", ndpId=" + ndpId);
+ }
+
+ WifiAwareNetworkSpecifier networkSpecifier = null;
+ AwareNetworkRequestInformation nnri = null;
+ for (Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> 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 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().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)));
+ if (DBG) {
+ Log.d(TAG, "onDataPathRequest: network request cache = " + mNetworkRequestsCache);
+ }
+ mMgr.respondToDataPathRequest(false, ndpId, "", null, null);
+ return null;
+ }
+
+ if (nnri.state != AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST) {
+ Log.w(TAG, "onDataPathRequest: request " + networkSpecifier + " is incorrect state="
+ + nnri.state);
+ mMgr.respondToDataPathRequest(false, ndpId, "", null, null);
+ mNetworkRequestsCache.remove(networkSpecifier);
+ return null;
+ }
+
+ nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE;
+ nnri.ndpId = ndpId;
+ nnri.interfaceName = selectInterfaceForRequest(nnri);
+ mMgr.respondToDataPathRequest(true, ndpId, nnri.interfaceName, nnri.networkSpecifier.pmk,
+ nnri.networkSpecifier.passphrase);
+
+ return networkSpecifier;
+ }
+
+ /**
+ * Called on the RESPONDER when the response to data-path request has been completed.
+ *
+ * @param ndpId The ID of the data-path (NDP)
+ * @param success Whether or not the 'RespondToDataPathRequest' operation was a success.
+ */
+ public void onRespondToDataPathRequest(int ndpId, boolean success) {
+ if (VDBG) {
+ Log.v(TAG, "onRespondToDataPathRequest: ndpId=" + ndpId + ", success=" + success);
+ }
+
+ WifiAwareNetworkSpecifier networkSpecifier = null;
+ AwareNetworkRequestInformation nnri = null;
+ for (Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> entry :
+ mNetworkRequestsCache.entrySet()) {
+ if (entry.getValue().ndpId == ndpId) {
+ networkSpecifier = entry.getKey();
+ nnri = entry.getValue();
+ break;
+ }
+ }
+
+ if (nnri == null) {
+ Log.w(TAG, "onRespondToDataPathRequest: can't find a request with specified ndpId="
+ + ndpId);
+ if (DBG) {
+ Log.d(TAG, "onRespondToDataPathRequest: network request cache = "
+ + mNetworkRequestsCache);
+ }
+ return;
+ }
+
+ if (!success) {
+ Log.w(TAG, "onRespondToDataPathRequest: request " + networkSpecifier
+ + " failed responding");
+ mMgr.endDataPath(ndpId);
+ mNetworkRequestsCache.remove(networkSpecifier);
+ return;
+ }
+
+ if (nnri.state
+ != AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_RESPOND_RESPONSE) {
+ Log.w(TAG, "onRespondToDataPathRequest: request " + networkSpecifier
+ + " is incorrect state=" + nnri.state);
+ mMgr.endDataPath(ndpId);
+ mNetworkRequestsCache.remove(networkSpecifier);
+ return;
+ }
+
+ nnri.state = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_CONFIRM;
+ }
+
+ /**
+ * 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.
+ * @return The network specifier of the data-path or a null if none/error.
+ */
+ public WifiAwareNetworkSpecifier onDataPathConfirm(int ndpId, byte[] mac, boolean accept,
+ int reason, byte[] message) {
+ if (VDBG) {
+ Log.v(TAG, "onDataPathConfirm: ndpId=" + ndpId + ", mac=" + String.valueOf(
+ HexEncoding.encode(mac)) + ", accept=" + accept + ", reason=" + reason);
+ }
+
+ Map.Entry<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> nnriE =
+ getNetworkRequestByNdpId(ndpId);
+ if (nnriE == null) {
+ Log.w(TAG, "onDataPathConfirm: network request not found for ndpId=" + ndpId);
+ if (accept) {
+ mMgr.endDataPath(ndpId);
+ }
+ return null;
+ }
+
+ WifiAwareNetworkSpecifier networkSpecifier = nnriE.getKey();
+ AwareNetworkRequestInformation nnri = nnriE.getValue();
+
+ // validate state
+ if (nnri.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ && nnri.state != AwareNetworkRequestInformation.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.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER
+ && nnri.state != AwareNetworkRequestInformation.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.networkSpecifier.role
+ == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR)
+ ? AwareNetworkRequestInformation.STATE_INITIATOR_CONFIRMED
+ : AwareNetworkRequestInformation.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 {
+ mNwService.setInterfaceUp(nnri.interfaceName);
+ mNwService.enableIpv6(nnri.interfaceName);
+ } catch (Exception e) { // NwService throws runtime exceptions for errors
+ 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 WifiAwareNetworkAgent(mLooper, mContext,
+ AGENT_TAG_PREFIX + nnri.ndpId,
+ new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORK_TAG, ""),
+ networkCapabilities, linkProperties, NETWORK_FACTORY_SCORE_AVAIL,
+ networkSpecifier, ndpId);
+ nnri.networkAgent.sendNetworkInfo(networkInfo);
+ } 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<WifiAwareNetworkSpecifier, AwareNetworkRequestInformation> 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 Aware comes down. Clean up all pending and up network requeests and agents.
+ */
+ public void onAwareDownCleanupDataPaths() {
+ if (VDBG) Log.v(TAG, "onAwareDownCleanupDataPaths");
+
+ for (AwareNetworkRequestInformation 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(NetworkSpecifier networkSpecifier) {
+ if (VDBG) Log.v(TAG, "handleDataPathTimeout: networkSpecifier=" + networkSpecifier);
+
+ AwareNetworkRequestInformation 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 WifiAwareNetworkFactory extends NetworkFactory {
+ WifiAwareNetworkFactory(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, "WifiAwareNetworkFactory.acceptRequest: request=" + request + ", score="
+ + score);
+ }
+
+ if (!mMgr.isUsageEnabled()) {
+ if (VDBG) {
+ Log.v(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ + " -- Aware disabled");
+ }
+ return false;
+ }
+
+ if (mInterfaces.isEmpty()) {
+ Log.w(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ + " -- No Aware interfaces are up");
+ return false;
+ }
+
+ NetworkSpecifier networkSpecifierBase =
+ request.networkCapabilities.getNetworkSpecifier();
+ if (!(networkSpecifierBase instanceof WifiAwareNetworkSpecifier)) {
+ Log.w(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ + " - not a WifiAwareNetworkSpecifier");
+ return false;
+ }
+
+ WifiAwareNetworkSpecifier networkSpecifier =
+ (WifiAwareNetworkSpecifier) networkSpecifierBase;
+
+ // look up specifier - are we being called again?
+ AwareNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
+ if (nnri != null) {
+ if (DBG) {
+ Log.d(TAG, "WifiAwareNetworkFactory.acceptRequest: request=" + request
+ + " - already in cache!?");
+ }
+
+ // seems to happen after a network agent is created - trying to rematch all
+ // requests again!?
+ return true;
+ }
+
+ // TODO: validate that the client ID actually comes from the correct process and is
+ // not faked?
+ nnri = AwareNetworkRequestInformation.processNetworkSpecifier(networkSpecifier, mMgr);
+ if (nnri == null) {
+ Log.e(TAG, "WifiAwareNetworkFactory.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, "WifiAwareNetworkFactory.needNetworkFor: networkRequest="
+ + networkRequest + ", score=" + score);
+ }
+
+ NetworkSpecifier networkSpecifierObj =
+ networkRequest.networkCapabilities.getNetworkSpecifier();
+ WifiAwareNetworkSpecifier networkSpecifier = null;
+ if (networkSpecifierObj instanceof WifiAwareNetworkSpecifier) {
+ networkSpecifier = (WifiAwareNetworkSpecifier) networkSpecifierObj;
+ }
+ AwareNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
+ if (nnri == null) {
+ Log.e(TAG, "WifiAwareNetworkFactory.needNetworkFor: networkRequest="
+ + networkRequest + " not in cache!?");
+ return;
+ }
+
+ if (nnri.networkSpecifier.role
+ == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
+ if (nnri.state != AwareNetworkRequestInformation.STATE_INITIATOR_IDLE) {
+ if (DBG) {
+ Log.d(TAG, "WifiAwareNetworkFactory.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.networkSpecifier.peerId,
+ NanDataPathChannelCfg.REQUEST_CHANNEL_SETUP, selectChannelForRequest(nnri),
+ nnri.peerDiscoveryMac, nnri.interfaceName, nnri.networkSpecifier.pmk,
+ nnri.networkSpecifier.passphrase);
+ nnri.state =
+ AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE;
+ } else {
+ if (nnri.state != AwareNetworkRequestInformation.STATE_RESPONDER_IDLE) {
+ if (DBG) {
+ Log.d(TAG, "WifiAwareNetworkFactory.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 = AwareNetworkRequestInformation.STATE_RESPONDER_WAIT_FOR_REQUEST;
+ }
+ }
+
+ @Override
+ protected void releaseNetworkFor(NetworkRequest networkRequest) {
+ if (VDBG) {
+ Log.v(TAG, "WifiAwareNetworkFactory.releaseNetworkFor: networkRequest="
+ + networkRequest);
+ }
+
+ NetworkSpecifier networkSpecifierObj =
+ networkRequest.networkCapabilities.getNetworkSpecifier();
+ WifiAwareNetworkSpecifier networkSpecifier = null;
+ if (networkSpecifierObj instanceof WifiAwareNetworkSpecifier) {
+ networkSpecifier = (WifiAwareNetworkSpecifier) networkSpecifierObj;
+ }
+
+ AwareNetworkRequestInformation nnri = mNetworkRequestsCache.get(networkSpecifier);
+ if (nnri == null) {
+ Log.e(TAG, "WifiAwareNetworkFactory.releaseNetworkFor: networkRequest="
+ + networkRequest + " not in cache!?");
+ return;
+ }
+
+ if (nnri.networkAgent != null) {
+ if (VDBG) {
+ Log.v(TAG, "WifiAwareNetworkFactory.releaseNetworkFor: networkRequest="
+ + networkRequest + ", nnri=" + nnri
+ + ": agent already created - deferring ending data-path to agent"
+ + ".unwanted()");
+ }
+ return;
+ }
+
+ if (nnri.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ && nnri.state
+ > AwareNetworkRequestInformation.STATE_INITIATOR_WAIT_FOR_REQUEST_RESPONSE) {
+ mMgr.endDataPath(nnri.ndpId);
+ }
+ if (nnri.networkSpecifier.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER
+ && nnri.state
+ > AwareNetworkRequestInformation.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 WifiAwareNetworkAgent extends NetworkAgent {
+ private NetworkInfo mNetworkInfo;
+ private WifiAwareNetworkSpecifier mNetworkSpecifier;
+ private int mNdpId;
+
+ WifiAwareNetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
+ NetworkCapabilities nc, LinkProperties lp, int score,
+ WifiAwareNetworkSpecifier 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, "WifiAwareNetworkAgent.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, "WifiAwareNetworkAgent.reconfigureAgentAsDisconnected: networkSpecifier="
+ + mNetworkSpecifier + ", ndpId=" + mNdpId);
+ }
+
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, "");
+ sendNetworkInfo(mNetworkInfo);
+ }
+ }
+
+ private void tearDownInterface(AwareNetworkRequestInformation nnri) {
+ if (VDBG) Log.v(TAG, "tearDownInterface: nnri=" + nnri);
+
+ if (nnri.interfaceName != null && !nnri.interfaceName.isEmpty()) {
+ try {
+ mNwService.setInterfaceDown(nnri.interfaceName);
+ } catch (Exception e) { // NwService throws runtime exceptions for errors
+ 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(AwareNetworkRequestInformation 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(AwareNetworkRequestInformation req) {
+ return 2437;
+ }
+
+ /**
+ * Aware network request. State object: contains network request information/state through its
+ * lifetime.
+ */
+ @VisibleForTesting
+ public static class AwareNetworkRequestInformation {
+ 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 uid;
+ public String interfaceName;
+ public int pubSubId = 0;
+ public byte[] peerDiscoveryMac = null;
+ public int ndpId;
+ public byte[] peerDataMac;
+ public WifiAwareNetworkSpecifier networkSpecifier;
+
+ public WifiAwareNetworkAgent networkAgent;
+
+ static AwareNetworkRequestInformation processNetworkSpecifier(WifiAwareNetworkSpecifier ns,
+ WifiAwareStateManager mgr) {
+ int uid, pubSubId = 0;
+ byte[] peerMac = ns.peerMac;
+
+ if (VDBG) {
+ Log.v(TAG, "processNetworkSpecifier: networkSpecifier=" + ns);
+ }
+
+ // type: always valid
+ if (ns.type < 0
+ || ns.type > WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_MAX_VALID) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns
+ + ", invalid 'type' value");
+ return null;
+ }
+
+ // role: always valid
+ if (ns.role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ && ns.role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns
+ + " -- invalid 'role' value");
+ return null;
+ }
+
+ if (ns.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ && ns.type != WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB
+ && ns.type != WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns
+ + " -- invalid 'type' value for INITIATOR (only IB and OOB are "
+ + "permitted)");
+ return null;
+ }
+
+ // look up network specifier information in Aware state manager
+ WifiAwareClientState client = mgr.getClient(ns.clientId);
+ if (client == null) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns
+ + " -- not client with this id -- clientId=" + ns.clientId);
+ return null;
+ }
+ uid = client.getUid();
+
+ // validate the role (if session ID provided: i.e. session 1xx)
+ if (ns.type == WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB
+ || ns.type == WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER) {
+ WifiAwareDiscoverySessionState session = client.getSession(ns.sessionId);
+ if (session == null) {
+ Log.e(TAG,
+ "processNetworkSpecifier: networkSpecifier=" + ns
+ + " -- no session with this id -- sessionId=" + ns.sessionId);
+ return null;
+ }
+
+ if ((session.isPublishSession()
+ && ns.role != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) || (
+ !session.isPublishSession() && ns.role
+ != WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR)) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns
+ + " -- invalid role for session type");
+ return null;
+ }
+
+ if (ns.type == WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB) {
+ pubSubId = session.getPubSubId();
+ String peerMacStr = session.getMac(ns.peerId, null);
+ if (peerMacStr == null) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns
+ + " -- no MAC address associated with this peer id -- peerId="
+ + ns.peerId);
+ return null;
+ }
+ try {
+ peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false);
+ if (peerMac == null || peerMac.length != 6) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier="
+ + ns + " -- invalid peer MAC address");
+ return null;
+ }
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "processNetworkSpecifier: networkSpecifier=" + ns
+ + " -- invalid peer MAC address -- e=" + e);
+ return null;
+ }
+ }
+ }
+
+ // create container and populate
+ AwareNetworkRequestInformation nnri = new AwareNetworkRequestInformation();
+ nnri.state = (ns.role == WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR)
+ ? AwareNetworkRequestInformation.STATE_INITIATOR_IDLE
+ : AwareNetworkRequestInformation.STATE_RESPONDER_IDLE;
+ nnri.uid = uid;
+ nnri.pubSubId = pubSubId;
+ nnri.peerDiscoveryMac = peerMac;
+ nnri.networkSpecifier = ns;
+
+ return nnri;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("AwareNetworkRequestInformation: ");
+ sb.append("state=").append(state).append(", ns=").append(networkSpecifier).append(
+ ", uid=").append(uid).append(", interfaceName=").append(interfaceName).append(
+ ", pubSubId=").append(pubSubId).append(", peerDiscoveryMac=").append(
+ peerDiscoveryMac == null ? ""
+ : String.valueOf(HexEncoding.encode(peerDiscoveryMac))).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(AwareNetworkRequestInformation nnri,
+ WifiAwareNetworkSpecifier 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("WifiAwareDataPathStateManager:");
+ 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/aware/WifiAwareDiscoverySessionState.java b/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java
new file mode 100644
index 0000000..d006aa8
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java
@@ -0,0 +1,276 @@
+/*
+ * 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.aware;
+
+import android.hardware.wifi.V1_0.NanStatusType;
+import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.SubscribeConfig;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import libcore.util.HexEncoding;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Manages the state of a single Aware 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 WifiAwareDiscoverySessionState {
+ private static final String TAG = "WifiAwareDiscSessState";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ private final WifiAwareNativeApi mWifiAwareNativeApi;
+ private int mSessionId;
+ private int mPubSubId;
+ private IWifiAwareDiscoverySessionCallback mCallback;
+ private boolean mIsPublishSession;
+
+ private final SparseArray<String> mMacByRequestorInstanceId = new SparseArray<>();
+
+ public WifiAwareDiscoverySessionState(WifiAwareNativeApi wifiAwareNativeApi, int sessionId,
+ int pubSubId, IWifiAwareDiscoverySessionCallback callback, boolean isPublishSession) {
+ mWifiAwareNativeApi = wifiAwareNativeApi;
+ mSessionId = sessionId;
+ mPubSubId = pubSubId;
+ mCallback = callback;
+ mIsPublishSession = isPublishSession;
+ }
+
+ public int getSessionId() {
+ return mSessionId;
+ }
+
+ public int getPubSubId() {
+ return mPubSubId;
+ }
+
+ public boolean isPublishSession() {
+ return mIsPublishSession;
+ }
+
+ public IWifiAwareDiscoverySessionCallback 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) {
+ mWifiAwareNativeApi.stopPublish((short) 0, mPubSubId);
+ } else {
+ mWifiAwareNativeApi.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 mPubSubId == pubSubId;
+ }
+
+ /**
+ * 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(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.e(TAG, "updatePublish: RemoteException=" + e);
+ }
+ return false;
+ }
+
+ boolean success = mWifiAwareNativeApi.publish(transactionId, mPubSubId, config);
+ if (!success) {
+ try {
+ mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.w(TAG, "updatePublish onSessionConfigFail(): RemoteException (FYI): " + e);
+ }
+ }
+
+ return success;
+ }
+
+ /**
+ * 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(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.e(TAG, "updateSubscribe: RemoteException=" + e);
+ }
+ return false;
+ }
+
+ boolean success = mWifiAwareNativeApi.subscribe(transactionId, mPubSubId, config);
+ if (!success) {
+ try {
+ mCallback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.w(TAG, "updateSubscribe onSessionConfigFail(): RemoteException (FYI): " + e);
+ }
+ }
+
+ return success;
+ }
+
+ /**
+ * 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 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 messageId) {
+ 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");
+ try {
+ mCallback.onMessageSendFail(messageId, NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendMessage: RemoteException=" + e);
+ }
+ return false;
+ }
+ byte[] peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false);
+
+ boolean success = mWifiAwareNativeApi.sendMessage(transactionId, mPubSubId, peerId, peerMac,
+ message, messageId);
+ if (!success) {
+ try {
+ mCallback.onMessageSendFail(messageId, NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendMessage: RemoteException=" + e);
+ }
+ return false;
+ }
+
+ return success;
+ }
+
+ /**
+ * 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 matchFilter The filter from the discovery advertisement (which was
+ * used in the match decision).
+ */
+ public void onMatch(int requestorInstanceId, byte[] peerMac, byte[] serviceSpecificInfo,
+ byte[] matchFilter) {
+ String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
+ mMacByRequestorInstanceId.put(requestorInstanceId, new String(HexEncoding.encode(peerMac)));
+
+ if (DBG) Log.d(TAG, "onMatch: previous peer MAC replaced - " + prevMac);
+
+ try {
+ mCallback.onMatch(requestorInstanceId, serviceSpecificInfo, matchFilter);
+ } 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.
+ */
+ public void onMessageReceived(int requestorInstanceId, byte[] peerMac, byte[] message) {
+ String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
+ mMacByRequestorInstanceId.put(requestorInstanceId, new String(HexEncoding.encode(peerMac)));
+
+ if (DBG) {
+ Log.d(TAG, "onMessageReceived: previous peer MAC replaced - " + prevMac);
+ }
+
+ try {
+ mCallback.onMessageReceived(requestorInstanceId, message);
+ } 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("AwareSessionState:");
+ pw.println(" mSessionId: " + mSessionId);
+ pw.println(" mIsPublishSession: " + mIsPublishSession);
+ pw.println(" mPubSubId: " + mPubSubId);
+ pw.println(" mMacByRequestorInstanceId: [" + mMacByRequestorInstanceId + "]");
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java
new file mode 100644
index 0000000..2efa2ae
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeApi.java
@@ -0,0 +1,825 @@
+/*
+ * 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.aware;
+
+import android.hardware.wifi.V1_0.IWifiNanIface;
+import android.hardware.wifi.V1_0.NanBandIndex;
+import android.hardware.wifi.V1_0.NanBandSpecificConfig;
+import android.hardware.wifi.V1_0.NanCipherSuiteType;
+import android.hardware.wifi.V1_0.NanConfigRequest;
+import android.hardware.wifi.V1_0.NanDataPathSecurityType;
+import android.hardware.wifi.V1_0.NanEnableRequest;
+import android.hardware.wifi.V1_0.NanInitiateDataPathRequest;
+import android.hardware.wifi.V1_0.NanMatchAlg;
+import android.hardware.wifi.V1_0.NanPublishRequest;
+import android.hardware.wifi.V1_0.NanRespondToDataPathIndicationRequest;
+import android.hardware.wifi.V1_0.NanSubscribeRequest;
+import android.hardware.wifi.V1_0.NanTransmitFollowupRequest;
+import android.hardware.wifi.V1_0.NanTxType;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.net.wifi.aware.ConfigRequest;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.SubscribeConfig;
+import android.os.RemoteException;
+import android.util.Log;
+
+import libcore.util.HexEncoding;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Translates Wi-Fi Aware requests from the framework to the HAL (HIDL).
+ *
+ * Delegates the management of the NAN interface to WifiAwareNativeManager.
+ */
+public class WifiAwareNativeApi {
+ private static final String TAG = "WifiAwareNativeApi";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ private final WifiAwareNativeManager mHal;
+
+ public WifiAwareNativeApi(WifiAwareNativeManager wifiAwareNativeManager) {
+ mHal = wifiAwareNativeManager;
+ }
+
+ /**
+ * Query the 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.v(TAG, "getCapabilities: transactionId=" + transactionId);
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "getCapabilities: null interface");
+ return false;
+ }
+
+ try {
+ WifiStatus status = iface.getCapabilitiesRequest(transactionId);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "getCapabilities: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getCapabilities: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Enable and configure Aware.
+ *
+ * @param transactionId Transaction ID for the transaction - used in the
+ * async callback to match with the original request.
+ * @param configRequest Requested Aware configuration.
+ * @param notifyIdentityChange Indicates whether or not to get address change callbacks.
+ * @param initialConfiguration Specifies whether initial configuration
+ * (true) or an update (false) to the configuration.
+ */
+ public boolean enableAndConfigure(short transactionId, ConfigRequest configRequest,
+ boolean notifyIdentityChange, boolean initialConfiguration) {
+ if (VDBG) {
+ Log.v(TAG, "enableAndConfigure: transactionId=" + transactionId + ", configRequest="
+ + configRequest + ", notifyIdentityChange=" + notifyIdentityChange
+ + ", initialConfiguration=" + initialConfiguration);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "enableAndConfigure: null interface");
+ return false;
+ }
+
+ try {
+ WifiStatus status;
+ if (initialConfiguration) {
+ // translate framework to HIDL configuration
+ NanEnableRequest req = new NanEnableRequest();
+
+ req.operateInBand[NanBandIndex.NAN_BAND_24GHZ] = true;
+ req.operateInBand[NanBandIndex.NAN_BAND_5GHZ] = configRequest.mSupport5gBand;
+ req.hopCountMax = 2;
+ req.configParams.masterPref = (byte) configRequest.mMasterPreference;
+ req.configParams.disableDiscoveryAddressChangeIndication = !notifyIdentityChange;
+ req.configParams.disableStartedClusterIndication = !notifyIdentityChange;
+ req.configParams.disableJoinedClusterIndication = !notifyIdentityChange;
+ req.configParams.includePublishServiceIdsInBeacon = true;
+ req.configParams.numberOfPublishServiceIdsInBeacon = 0;
+ req.configParams.includeSubscribeServiceIdsInBeacon = true;
+ req.configParams.numberOfSubscribeServiceIdsInBeacon = 0;
+ req.configParams.rssiWindowSize = 8;
+ req.configParams.macAddressRandomizationIntervalSec = 1800;
+
+ NanBandSpecificConfig config24 = new NanBandSpecificConfig();
+ config24.rssiClose = 60;
+ config24.rssiMiddle = 70;
+ config24.rssiCloseProximity = 60;
+ config24.dwellTimeMs = (byte) 200;
+ config24.scanPeriodSec = 20;
+ if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]
+ == ConfigRequest.DW_INTERVAL_NOT_INIT) {
+ config24.validDiscoveryWindowIntervalVal = false;
+ } else {
+ config24.validDiscoveryWindowIntervalVal = true;
+ config24.discoveryWindowIntervalVal =
+ (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
+ .NAN_BAND_24GHZ];
+ }
+ req.configParams.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ] = config24;
+
+ NanBandSpecificConfig config5 = new NanBandSpecificConfig();
+ config5.rssiClose = 60;
+ config5.rssiMiddle = 75;
+ config5.rssiCloseProximity = 60;
+ config5.dwellTimeMs = (byte) 200;
+ config5.scanPeriodSec = 20;
+ if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]
+ == ConfigRequest.DW_INTERVAL_NOT_INIT) {
+ config5.validDiscoveryWindowIntervalVal = false;
+ } else {
+ config5.validDiscoveryWindowIntervalVal = true;
+ config5.discoveryWindowIntervalVal =
+ (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
+ .NAN_BAND_5GHZ];
+ }
+ req.configParams.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ] = config5;
+
+ req.debugConfigs.validClusterIdVals = true;
+ req.debugConfigs.clusterIdTopRangeVal = (short) configRequest.mClusterHigh;
+ req.debugConfigs.clusterIdBottomRangeVal = (short) configRequest.mClusterLow;
+ req.debugConfigs.validIntfAddrVal = false;
+ req.debugConfigs.validOuiVal = false;
+ req.debugConfigs.ouiVal = 0;
+ req.debugConfigs.validRandomFactorForceVal = false;
+ req.debugConfigs.randomFactorForceVal = 0;
+ req.debugConfigs.validHopCountForceVal = false;
+ req.debugConfigs.hopCountForceVal = 0;
+ req.debugConfigs.validDiscoveryChannelVal = false;
+ req.debugConfigs.discoveryChannelMhzVal[NanBandIndex.NAN_BAND_24GHZ] = 0;
+ req.debugConfigs.discoveryChannelMhzVal[NanBandIndex.NAN_BAND_5GHZ] = 0;
+ req.debugConfigs.validUseBeaconsInBandVal = false;
+ req.debugConfigs.useBeaconsInBandVal[NanBandIndex.NAN_BAND_24GHZ] = true;
+ req.debugConfigs.useBeaconsInBandVal[NanBandIndex.NAN_BAND_5GHZ] = true;
+ req.debugConfigs.validUseSdfInBandVal = false;
+ req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_24GHZ] = true;
+ req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_5GHZ] = true;
+
+ status = iface.enableRequest(transactionId, req);
+ } else {
+ NanConfigRequest req = new NanConfigRequest();
+ req.masterPref = (byte) configRequest.mMasterPreference;
+ req.disableDiscoveryAddressChangeIndication = !notifyIdentityChange;
+ req.disableStartedClusterIndication = !notifyIdentityChange;
+ req.disableJoinedClusterIndication = !notifyIdentityChange;
+ req.includePublishServiceIdsInBeacon = true;
+ req.numberOfPublishServiceIdsInBeacon = 0;
+ req.includeSubscribeServiceIdsInBeacon = true;
+ req.numberOfSubscribeServiceIdsInBeacon = 0;
+ req.rssiWindowSize = 8;
+ req.macAddressRandomizationIntervalSec = 1800;
+
+ NanBandSpecificConfig config24 = new NanBandSpecificConfig();
+ config24.rssiClose = 60;
+ config24.rssiMiddle = 70;
+ config24.rssiCloseProximity = 60;
+ config24.dwellTimeMs = (byte) 200;
+ config24.scanPeriodSec = 20;
+ if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]
+ == ConfigRequest.DW_INTERVAL_NOT_INIT) {
+ config24.validDiscoveryWindowIntervalVal = false;
+ } else {
+ config24.validDiscoveryWindowIntervalVal = true;
+ config24.discoveryWindowIntervalVal =
+ (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
+ .NAN_BAND_24GHZ];
+ }
+ req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ] = config24;
+
+ NanBandSpecificConfig config5 = new NanBandSpecificConfig();
+ config5.rssiClose = 60;
+ config5.rssiMiddle = 75;
+ config5.rssiCloseProximity = 60;
+ config5.dwellTimeMs = (byte) 200;
+ config5.scanPeriodSec = 20;
+ if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]
+ == ConfigRequest.DW_INTERVAL_NOT_INIT) {
+ config5.validDiscoveryWindowIntervalVal = false;
+ } else {
+ config5.validDiscoveryWindowIntervalVal = true;
+ config5.discoveryWindowIntervalVal =
+ (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
+ .NAN_BAND_5GHZ];
+ }
+ req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ] = config5;
+
+ status = iface.configRequest(transactionId, req);
+ }
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "enableAndConfigure: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "enableAndConfigure: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Disable Aware.
+ *
+ * @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, "disable");
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "disable: null interface");
+ return false;
+ }
+
+ try {
+ WifiStatus status = iface.disableRequest(transactionId);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "disable: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "disable: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * 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 + ", config=" + publishConfig);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "publish: null interface");
+ return false;
+ }
+
+ NanPublishRequest req = new NanPublishRequest();
+ req.baseConfigs.sessionId = 0;
+ req.baseConfigs.ttlSec = (short) publishConfig.mTtlSec;
+ req.baseConfigs.discoveryWindowPeriod = 1;
+ req.baseConfigs.discoveryCount = 0;
+ convertNativeByteArrayToArrayList(publishConfig.mServiceName, req.baseConfigs.serviceName);
+ // TODO: what's the right value on publish?
+ req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_ONCE;
+ convertNativeByteArrayToArrayList(publishConfig.mServiceSpecificInfo,
+ req.baseConfigs.serviceSpecificInfo);
+ convertNativeByteArrayToArrayList(publishConfig.mMatchFilter,
+ publishConfig.mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED
+ ? req.baseConfigs.txMatchFilter : req.baseConfigs.rxMatchFilter);
+ req.baseConfigs.useRssiThreshold = false;
+ req.baseConfigs.disableDiscoveryTerminationIndication =
+ !publishConfig.mEnableTerminateNotification;
+ req.baseConfigs.disableMatchExpirationIndication = true;
+ req.baseConfigs.disableFollowupReceivedIndication = false;
+
+ // TODO: configure ranging and security
+ req.baseConfigs.securityConfig.securityType = NanDataPathSecurityType.OPEN;
+ req.baseConfigs.rangingRequired = false;
+ req.autoAcceptDataPathRequests = false;
+
+ req.publishType = publishConfig.mPublishType;
+ req.txType = NanTxType.BROADCAST;
+
+ try {
+ WifiStatus status = iface.startPublishRequest(transactionId, req);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "publish: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "publish: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * 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 + ", config=" + subscribeConfig);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "subscribe: null interface");
+ return false;
+ }
+
+ NanSubscribeRequest req = new NanSubscribeRequest();
+ req.baseConfigs.sessionId = 0;
+ req.baseConfigs.ttlSec = (short) subscribeConfig.mTtlSec;
+ req.baseConfigs.discoveryWindowPeriod = 1;
+ req.baseConfigs.discoveryCount = 0;
+ convertNativeByteArrayToArrayList(subscribeConfig.mServiceName,
+ req.baseConfigs.serviceName);
+ req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_ONCE;
+ convertNativeByteArrayToArrayList(subscribeConfig.mServiceSpecificInfo,
+ req.baseConfigs.serviceSpecificInfo);
+ convertNativeByteArrayToArrayList(subscribeConfig.mMatchFilter,
+ subscribeConfig.mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE
+ ? req.baseConfigs.txMatchFilter : req.baseConfigs.rxMatchFilter);
+ req.baseConfigs.useRssiThreshold = false;
+ req.baseConfigs.disableDiscoveryTerminationIndication =
+ !subscribeConfig.mEnableTerminateNotification;
+ req.baseConfigs.disableMatchExpirationIndication = true;
+ req.baseConfigs.disableFollowupReceivedIndication = false;
+
+ // TODO: configure ranging and security
+ req.baseConfigs.securityConfig.securityType = NanDataPathSecurityType.OPEN;
+ req.baseConfigs.rangingRequired = false;
+
+ req.subscribeType = subscribeConfig.mSubscribeType;
+
+ try {
+ WifiStatus status = iface.startSubscribeRequest(transactionId, req);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "subscribe: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "subscribe: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * 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 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 messageId) {
+ if (VDBG) {
+ Log.d(TAG,
+ "sendMessage: transactionId=" + transactionId + ", pubSubId=" + pubSubId
+ + ", requestorInstanceId=" + requestorInstanceId + ", dest="
+ + String.valueOf(HexEncoding.encode(dest)) + ", messageId="
+ + messageId);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "sendMessage: null interface");
+ return false;
+ }
+
+ NanTransmitFollowupRequest req = new NanTransmitFollowupRequest();
+ req.discoverySessionId = (byte) pubSubId;
+ req.peerId = requestorInstanceId;
+ copyArray(dest, req.addr);
+ req.isHighPriority = false;
+ req.shouldUseDiscoveryWindow = true;
+ convertNativeByteArrayToArrayList(message, req.serviceSpecificInfo);
+ req.disableFollowupResultIndication = false;
+
+ try {
+ WifiStatus status = iface.transmitFollowupRequest(transactionId, req);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "sendMessage: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "sendMessage: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "stopPublish: null interface");
+ return false;
+ }
+
+ try {
+ WifiStatus status = iface.stopPublishRequest(transactionId, (byte) pubSubId);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "stopPublish: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "stopPublish: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "stopSubscribe: null interface");
+ return false;
+ }
+
+ try {
+ WifiStatus status = iface.stopSubscribeRequest(transactionId, (byte) pubSubId);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "stopSubscribe: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "stopSubscribe: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Create a Aware 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. "aware0".
+ */
+ public boolean createAwareNetworkInterface(short transactionId, String interfaceName) {
+ if (VDBG) {
+ Log.v(TAG, "createAwareNetworkInterface: transactionId=" + transactionId + ", "
+ + "interfaceName=" + interfaceName);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "createAwareNetworkInterface: null interface");
+ return false;
+ }
+
+ try {
+ WifiStatus status = iface.createDataInterfaceRequest(transactionId, interfaceName);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "createAwareNetworkInterface: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "createAwareNetworkInterface: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Deletes a Aware 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. "aware0".
+ */
+ public boolean deleteAwareNetworkInterface(short transactionId, String interfaceName) {
+ if (VDBG) {
+ Log.v(TAG, "deleteAwareNetworkInterface: transactionId=" + transactionId + ", "
+ + "interfaceName=" + interfaceName);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "deleteAwareNetworkInterface: null interface");
+ return false;
+ }
+
+ try {
+ WifiStatus status = iface.deleteDataInterfaceRequest(transactionId, interfaceName);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "deleteAwareNetworkInterface: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "deleteAwareNetworkInterface: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Initiates setting up a data-path between device and peer. Security is provided by either
+ * PMK or Passphrase (not both) - if both are null then an open (unencrypted) link is set up.
+ *
+ * @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 pmk Pairwise master key (PMK - see IEEE 802.11i) for the data-path.
+ * @param passphrase Passphrase for the data-path.
+ * @param capabilities The capabilities of the firmware.
+ */
+ public boolean initiateDataPath(short transactionId, int peerId, int channelRequestType,
+ int channel, byte[] peer, String interfaceName, byte[] pmk, String passphrase,
+ Capabilities capabilities) {
+ if (VDBG) {
+ Log.v(TAG, "initiateDataPath: transactionId=" + transactionId + ", peerId=" + peerId
+ + ", channelRequestType=" + channelRequestType + ", channel=" + channel
+ + ", peer=" + String.valueOf(HexEncoding.encode(peer)) + ", interfaceName="
+ + interfaceName);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "initiateDataPath: null interface");
+ return false;
+ }
+
+ if (capabilities == null) {
+ Log.e(TAG, "initiateDataPath: null capabilities");
+ return false;
+ }
+
+ NanInitiateDataPathRequest req = new NanInitiateDataPathRequest();
+ req.peerId = peerId;
+ copyArray(peer, req.peerDiscMacAddr);
+ req.channelRequestType = channelRequestType;
+ req.channel = channel;
+ req.ifaceName = interfaceName;
+ req.securityConfig.securityType = NanDataPathSecurityType.OPEN;
+ if (pmk != null && pmk.length != 0) {
+ req.securityConfig.cipherType = getStrongestCipherSuiteType(
+ capabilities.supportedCipherSuites);
+ req.securityConfig.securityType = NanDataPathSecurityType.PMK;
+ copyArray(pmk, req.securityConfig.pmk);
+ }
+ if (passphrase != null && passphrase.length() != 0) {
+ req.securityConfig.cipherType = getStrongestCipherSuiteType(
+ capabilities.supportedCipherSuites);
+ req.securityConfig.securityType = NanDataPathSecurityType.PASSPHRASE;
+ convertNativeByteArrayToArrayList(passphrase.getBytes(), req.securityConfig.passphrase);
+ }
+
+ try {
+ WifiStatus status = iface.initiateDataPathRequest(transactionId, req);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "initiateDataPath: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "initiateDataPath: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * Responds to a data request from a peer. Security is provided by either PMK or Passphrase (not
+ * both) - if both are null then an open (unencrypted) link is set up.
+ *
+ * @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 (Aware 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 pmk Pairwise master key (PMK - see IEEE 802.11i) for the data-path.
+ * @param passphrase Passphrase for the data-path.
+ * @param capabilities The capabilities of the firmware.
+ */
+ public boolean respondToDataPathRequest(short transactionId, boolean accept, int ndpId,
+ String interfaceName, byte[] pmk, String passphrase, Capabilities capabilities) {
+ if (VDBG) {
+ Log.v(TAG, "respondToDataPathRequest: transactionId=" + transactionId + ", accept="
+ + accept + ", int ndpId=" + ndpId + ", interfaceName=" + interfaceName);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "respondToDataPathRequest: null interface");
+ return false;
+ }
+
+ if (capabilities == null) {
+ Log.e(TAG, "initiateDataPath: null capabilities");
+ return false;
+ }
+
+ NanRespondToDataPathIndicationRequest req = new NanRespondToDataPathIndicationRequest();
+ req.acceptRequest = accept;
+ req.ndpInstanceId = ndpId;
+ req.ifaceName = interfaceName;
+ req.securityConfig.securityType = NanDataPathSecurityType.OPEN;
+ if (pmk != null && pmk.length != 0) {
+ req.securityConfig.cipherType = getStrongestCipherSuiteType(
+ capabilities.supportedCipherSuites);
+ req.securityConfig.securityType = NanDataPathSecurityType.PMK;
+ copyArray(pmk, req.securityConfig.pmk);
+ }
+ if (passphrase != null && passphrase.length() != 0) {
+ req.securityConfig.cipherType = getStrongestCipherSuiteType(
+ capabilities.supportedCipherSuites);
+ req.securityConfig.securityType = NanDataPathSecurityType.PASSPHRASE;
+ convertNativeByteArrayToArrayList(passphrase.getBytes(), req.securityConfig.passphrase);
+ }
+
+ try {
+ WifiStatus status = iface.respondToDataPathIndicationRequest(transactionId, req);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "respondToDataPathRequest: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "respondToDataPathRequest: exception: " + e);
+ return false;
+ }
+ }
+
+ /**
+ * 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 (Aware data path) ID to be terminated.
+ */
+ public boolean endDataPath(short transactionId, int ndpId) {
+ if (VDBG) {
+ Log.v(TAG, "endDataPath: transactionId=" + transactionId + ", ndpId=" + ndpId);
+ }
+
+ IWifiNanIface iface = mHal.getWifiNanIface();
+ if (iface == null) {
+ Log.e(TAG, "endDataPath: null interface");
+ return false;
+ }
+
+ try {
+ WifiStatus status = iface.terminateDataPathRequest(transactionId, ndpId);
+ if (status.code == WifiStatusCode.SUCCESS) {
+ return true;
+ } else {
+ Log.e(TAG, "endDataPath: error: " + statusString(status));
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "endDataPath: exception: " + e);
+ return false;
+ }
+ }
+
+
+ // utilities
+
+ /**
+ * Returns the strongest supported cipher suite.
+ *
+ * Baseline is very simple: 256 > 128 > 0.
+ */
+ private int getStrongestCipherSuiteType(int supportedCipherSuites) {
+ if ((supportedCipherSuites & NanCipherSuiteType.SHARED_KEY_256_MASK) != 0) {
+ return NanCipherSuiteType.SHARED_KEY_256_MASK;
+ }
+ if ((supportedCipherSuites & NanCipherSuiteType.SHARED_KEY_128_MASK) != 0) {
+ return NanCipherSuiteType.SHARED_KEY_128_MASK;
+ }
+ return NanCipherSuiteType.NONE;
+ }
+
+ /**
+ * Converts a byte[] to an ArrayList<Byte>. Fills in the entries of the 'to' array if
+ * provided (non-null), otherwise creates and returns a new ArrayList<>.
+ *
+ * @param from The input byte[] to convert from.
+ * @param to An optional ArrayList<> to fill in from 'from'.
+ *
+ * @return A newly allocated ArrayList<> if 'to' is null, otherwise null.
+ */
+ private ArrayList<Byte> convertNativeByteArrayToArrayList(byte[] from, ArrayList<Byte> to) {
+ if (from == null) {
+ from = new byte[0];
+ }
+
+ if (to == null) {
+ to = new ArrayList<>(from.length);
+ } else {
+ to.ensureCapacity(from.length);
+ }
+ for (int i = 0; i < from.length; ++i) {
+ to.add(from[i]);
+ }
+ return to;
+ }
+
+ private void copyArray(byte[] from, byte[] to) {
+ if (from == null || to == null || from.length != to.length) {
+ Log.e(TAG, "copyArray error: from=" + from + ", to=" + to);
+ return;
+ }
+ for (int i = 0; i < from.length; ++i) {
+ to[i] = from[i];
+ }
+ }
+
+ private static String statusString(WifiStatus status) {
+ if (status == null) {
+ return "status=null";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(status.code).append(" (").append(status.description).append(")");
+ return sb.toString();
+ }
+
+ /**
+ * Dump the internal state of the class.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ mHal.dump(fd, pw, args);
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
new file mode 100644
index 0000000..6f1925f
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.aware;
+
+import android.hardware.wifi.V1_0.IWifiNanIfaceEventCallback;
+import android.hardware.wifi.V1_0.NanCapabilities;
+import android.hardware.wifi.V1_0.NanClusterEventInd;
+import android.hardware.wifi.V1_0.NanClusterEventType;
+import android.hardware.wifi.V1_0.NanDataPathConfirmInd;
+import android.hardware.wifi.V1_0.NanDataPathRequestInd;
+import android.hardware.wifi.V1_0.NanFollowupReceivedInd;
+import android.hardware.wifi.V1_0.NanMatchInd;
+import android.hardware.wifi.V1_0.NanStatusType;
+import android.hardware.wifi.V1_0.WifiNanStatus;
+import android.util.Log;
+
+import libcore.util.HexEncoding;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Manages the callbacks from Wi-Fi Aware HIDL (HAL).
+ */
+public class WifiAwareNativeCallback extends IWifiNanIfaceEventCallback.Stub {
+ private static final String TAG = "WifiAwareNativeCallback";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
+
+ private final WifiAwareStateManager mWifiAwareStateManager;
+
+ public WifiAwareNativeCallback(WifiAwareStateManager wifiAwareStateManager) {
+ mWifiAwareStateManager = wifiAwareStateManager;
+ }
+
+ @Override
+ public void notifyCapabilitiesResponse(short id, WifiNanStatus status,
+ NanCapabilities capabilities) {
+ if (VDBG) {
+ Log.v(TAG, "notifyCapabilitiesResponse: id=" + id + ", status=" + statusString(status)
+ + ", capabilities=" + capabilities);
+ }
+
+ if (status.status == NanStatusType.SUCCESS) {
+ Capabilities frameworkCapabilities = new Capabilities();
+ frameworkCapabilities.maxConcurrentAwareClusters = capabilities.maxConcurrentClusters;
+ frameworkCapabilities.maxPublishes = capabilities.maxPublishes;
+ frameworkCapabilities.maxSubscribes = capabilities.maxSubscribes;
+ frameworkCapabilities.maxServiceNameLen = capabilities.maxServiceNameLen;
+ frameworkCapabilities.maxMatchFilterLen = capabilities.maxMatchFilterLen;
+ frameworkCapabilities.maxTotalMatchFilterLen = capabilities.maxTotalMatchFilterLen;
+ frameworkCapabilities.maxServiceSpecificInfoLen =
+ capabilities.maxServiceSpecificInfoLen;
+ frameworkCapabilities.maxExtendedServiceSpecificInfoLen =
+ capabilities.maxExtendedServiceSpecificInfoLen;
+ frameworkCapabilities.maxNdiInterfaces = capabilities.maxNdiInterfaces;
+ frameworkCapabilities.maxNdpSessions = capabilities.maxNdpSessions;
+ frameworkCapabilities.maxAppInfoLen = capabilities.maxAppInfoLen;
+ frameworkCapabilities.maxQueuedTransmitMessages =
+ capabilities.maxQueuedTransmitFollowupMsgs;
+ frameworkCapabilities.maxSubscribeInterfaceAddresses =
+ capabilities.maxSubscribeInterfaceAddresses;
+ frameworkCapabilities.supportedCipherSuites = capabilities.supportedCipherSuites;
+
+ mWifiAwareStateManager.onCapabilitiesUpdateResponse(id, frameworkCapabilities);
+ } else {
+ Log.e(TAG, "notifyCapabilitiesResponse: error code=" + status.status + " ("
+ + status.description + ")");
+ }
+ }
+
+ @Override
+ public void notifyEnableResponse(short id, WifiNanStatus status) {
+ if (VDBG) Log.v(TAG, "notifyEnableResponse: id=" + id + ", status=" + statusString(status));
+
+ if (status.status == NanStatusType.SUCCESS) {
+ mWifiAwareStateManager.onConfigSuccessResponse(id);
+ } else {
+ mWifiAwareStateManager.onConfigFailedResponse(id, status.status);
+ }
+ }
+
+ @Override
+ public void notifyConfigResponse(short id, WifiNanStatus status) {
+ if (VDBG) Log.v(TAG, "notifyConfigResponse: id=" + id + ", status=" + statusString(status));
+
+ if (status.status == NanStatusType.SUCCESS) {
+ mWifiAwareStateManager.onConfigSuccessResponse(id);
+ } else {
+ mWifiAwareStateManager.onConfigFailedResponse(id, status.status);
+ }
+ }
+
+ @Override
+ public void notifyDisableResponse(short id, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "notifyDisableResponse: id=" + id + ", status=" + statusString(status));
+ }
+
+ if (status.status == NanStatusType.SUCCESS) {
+ // NOP
+ } else {
+ Log.e(TAG, "notifyDisableResponse: failure - code=" + status.status + " ("
+ + status.description + ")");
+ }
+ }
+
+ @Override
+ public void notifyStartPublishResponse(short id, WifiNanStatus status, byte publishId) {
+ if (VDBG) {
+ Log.v(TAG, "notifyStartPublishResponse: id=" + id + ", status=" + statusString(status)
+ + ", publishId=" + publishId);
+ }
+
+ if (status.status == NanStatusType.SUCCESS) {
+ mWifiAwareStateManager.onSessionConfigSuccessResponse(id, true, publishId);
+ } else {
+ mWifiAwareStateManager.onSessionConfigFailResponse(id, true, status.status);
+ }
+ }
+
+ @Override
+ public void notifyStopPublishResponse(short id, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "notifyStopPublishResponse: id=" + id + ", status=" + statusString(status));
+ }
+
+ if (status.status == NanStatusType.SUCCESS) {
+ // NOP
+ } else {
+ Log.e(TAG, "notifyStopPublishResponse: failure - code=" + status.status + " ("
+ + status.description + ")");
+ }
+ }
+
+ @Override
+ public void notifyStartSubscribeResponse(short id, WifiNanStatus status, byte subscribeId) {
+ if (VDBG) {
+ Log.v(TAG, "notifyStartSubscribeResponse: id=" + id + ", status=" + statusString(status)
+ + ", subscribeId=" + subscribeId);
+ }
+
+ if (status.status == NanStatusType.SUCCESS) {
+ mWifiAwareStateManager.onSessionConfigSuccessResponse(id, false, subscribeId);
+ } else {
+ mWifiAwareStateManager.onSessionConfigFailResponse(id, false, status.status);
+ }
+ }
+
+ @Override
+ public void notifyStopSubscribeResponse(short id, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "notifyStopSubscribeResponse: id=" + id + ", status="
+ + statusString(status));
+ }
+
+ if (status.status == NanStatusType.SUCCESS) {
+ // NOP
+ } else {
+ Log.e(TAG, "notifyStopSubscribeResponse: failure - code=" + status.status + " ("
+ + status.description + ")");
+ }
+ }
+
+ @Override
+ public void notifyTransmitFollowupResponse(short id, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "notifyTransmitFollowupResponse: id=" + id + ", status="
+ + statusString(status));
+ }
+
+ if (status.status == NanStatusType.SUCCESS) {
+ mWifiAwareStateManager.onMessageSendQueuedSuccessResponse(id);
+ } else {
+ mWifiAwareStateManager.onMessageSendQueuedFailResponse(id, status.status);
+ }
+ }
+
+ @Override
+ public void notifyCreateDataInterfaceResponse(short id, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "notifyCreateDataInterfaceResponse: id=" + id + ", status="
+ + statusString(status));
+ }
+
+ mWifiAwareStateManager.onCreateDataPathInterfaceResponse(id,
+ status.status == NanStatusType.SUCCESS, status.status);
+ }
+
+ @Override
+ public void notifyDeleteDataInterfaceResponse(short id, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "notifyDeleteDataInterfaceResponse: id=" + id + ", status="
+ + statusString(status));
+ }
+
+ mWifiAwareStateManager.onDeleteDataPathInterfaceResponse(id,
+ status.status == NanStatusType.SUCCESS, status.status);
+ }
+
+ @Override
+ public void notifyInitiateDataPathResponse(short id, WifiNanStatus status,
+ int ndpInstanceId) {
+ if (VDBG) {
+ Log.v(TAG, "notifyInitiateDataPathResponse: id=" + id + ", status="
+ + statusString(status) + ", ndpInstanceId=" + ndpInstanceId);
+ }
+
+ if (status.status == NanStatusType.SUCCESS) {
+ mWifiAwareStateManager.onInitiateDataPathResponseSuccess(id, ndpInstanceId);
+ } else {
+ mWifiAwareStateManager.onInitiateDataPathResponseFail(id, status.status);
+ }
+ }
+
+ @Override
+ public void notifyRespondToDataPathIndicationResponse(short id, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "notifyRespondToDataPathIndicationResponse: id=" + id
+ + ", status=" + statusString(status));
+ }
+
+ mWifiAwareStateManager.onRespondToDataPathSetupRequestResponse(id,
+ status.status == NanStatusType.SUCCESS, status.status);
+ }
+
+ @Override
+ public void notifyTerminateDataPathResponse(short id, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "notifyTerminateDataPathResponse: id=" + id + ", status="
+ + statusString(status));
+ }
+
+ mWifiAwareStateManager.onEndDataPathResponse(id, status.status == NanStatusType.SUCCESS,
+ status.status);
+ }
+
+ @Override
+ public void eventClusterEvent(NanClusterEventInd event) {
+ if (VDBG) {
+ Log.v(TAG, "eventClusterEvent: eventType=" + event.eventType + ", addr="
+ + String.valueOf(HexEncoding.encode(event.addr)));
+ }
+
+ if (event.eventType == NanClusterEventType.DISCOVERY_MAC_ADDRESS_CHANGED) {
+ mWifiAwareStateManager.onInterfaceAddressChangeNotification(event.addr);
+ } else if (event.eventType == NanClusterEventType.STARTED_CLUSTER) {
+ mWifiAwareStateManager.onClusterChangeNotification(
+ WifiAwareClientState.CLUSTER_CHANGE_EVENT_STARTED, event.addr);
+ } else if (event.eventType == NanClusterEventType.JOINED_CLUSTER) {
+ mWifiAwareStateManager.onClusterChangeNotification(
+ WifiAwareClientState.CLUSTER_CHANGE_EVENT_JOINED, event.addr);
+ } else {
+ Log.e(TAG, "eventClusterEvent: invalid eventType=" + event.eventType);
+ }
+ }
+
+ @Override
+ public void eventDisabled(WifiNanStatus status) {
+ if (VDBG) Log.v(TAG, "eventDisabled: status=" + statusString(status));
+
+ mWifiAwareStateManager.onAwareDownNotification(status.status);
+ }
+
+ @Override
+ public void eventPublishTerminated(byte sessionId, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "eventPublishTerminated: sessionId=" + sessionId + ", status="
+ + statusString(status));
+ }
+
+ mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, true);
+ }
+
+ @Override
+ public void eventSubscribeTerminated(byte sessionId, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "eventSubscribeTerminated: sessionId=" + sessionId + ", status="
+ + statusString(status));
+ }
+
+ mWifiAwareStateManager.onSessionTerminatedNotification(sessionId, status.status, false);
+ }
+
+ @Override
+ public void eventMatch(NanMatchInd event) {
+ if (VDBG) {
+ Log.v(TAG, "eventMatch: discoverySessionId=" + event.discoverySessionId + ", peerId="
+ + event.peerId + ", addr=" + String.valueOf(HexEncoding.encode(event.addr))
+ + ", serviceSpecificInfo=" + Arrays.toString(
+ convertArrayListToNativeByteArray(event.serviceSpecificInfo)) + ", matchFilter="
+ + Arrays.toString(convertArrayListToNativeByteArray(event.matchFilter)));
+ }
+
+ mWifiAwareStateManager.onMatchNotification(event.discoverySessionId, event.peerId,
+ event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo),
+ convertArrayListToNativeByteArray(event.matchFilter));
+ }
+
+ @Override
+ public void eventMatchExpired(byte discoverySessionId, int peerId) {
+ if (VDBG) {
+ Log.v(TAG, "eventMatchExpired: discoverySessionId=" + discoverySessionId
+ + ", peerId=" + peerId);
+ }
+
+ // NOP
+ }
+
+ @Override
+ public void eventFollowupReceived(NanFollowupReceivedInd event) {
+ if (VDBG) {
+ Log.v(TAG, "eventFollowupReceived: discoverySessionId=" + event.discoverySessionId
+ + ", peerId=" + event.peerId + ", addr=" + String.valueOf(
+ HexEncoding.encode(event.addr)));
+ }
+
+ mWifiAwareStateManager.onMessageReceivedNotification(event.discoverySessionId, event.peerId,
+ event.addr, convertArrayListToNativeByteArray(event.serviceSpecificInfo));
+ }
+
+ @Override
+ public void eventTransmitFollowup(short id, WifiNanStatus status) {
+ if (VDBG) {
+ Log.v(TAG, "eventTransmitFollowup: id=" + id + ", status=" + statusString(status));
+ }
+
+ if (status.status == NanStatusType.SUCCESS) {
+ mWifiAwareStateManager.onMessageSendSuccessNotification(id);
+ } else {
+ mWifiAwareStateManager.onMessageSendFailNotification(id, status.status);
+ }
+ }
+
+ @Override
+ public void eventDataPathRequest(NanDataPathRequestInd event) {
+ if (VDBG) {
+ Log.v(TAG, "eventDataPathRequest: discoverySessionId=" + event.discoverySessionId
+ + ", peerDiscMacAddr=" + String.valueOf(
+ HexEncoding.encode(event.peerDiscMacAddr)) + ", ndpInstanceId="
+ + event.ndpInstanceId);
+ }
+
+ mWifiAwareStateManager.onDataPathRequestNotification(event.discoverySessionId,
+ event.peerDiscMacAddr, event.ndpInstanceId);
+ }
+
+ @Override
+ public void eventDataPathConfirm(NanDataPathConfirmInd event) {
+ if (VDBG) {
+ Log.v(TAG, "onDataPathConfirm: ndpInstanceId=" + event.ndpInstanceId
+ + ", peerNdiMacAddr=" + String.valueOf(HexEncoding.encode(event.peerNdiMacAddr))
+ + ", dataPathSetupSuccess=" + event.dataPathSetupSuccess + ", reason="
+ + event.status.status);
+ }
+
+ mWifiAwareStateManager.onDataPathConfirmNotification(event.ndpInstanceId,
+ event.peerNdiMacAddr, event.dataPathSetupSuccess, event.status.status,
+ convertArrayListToNativeByteArray(event.appInfo));
+ }
+
+ @Override
+ public void eventDataPathTerminated(int ndpInstanceId) {
+ if (VDBG) Log.v(TAG, "eventDataPathTerminated: ndpInstanceId=" + ndpInstanceId);
+
+ mWifiAwareStateManager.onDataPathEndNotification(ndpInstanceId);
+ }
+
+ // utilities
+
+ /**
+ * Converts an ArrayList<Byte> to a byte[].
+ *
+ * @param from The input ArrayList<Byte></Byte> to convert from.
+ *
+ * @return A newly allocated byte[].
+ */
+ private byte[] convertArrayListToNativeByteArray(ArrayList<Byte> from) {
+ if (from == null) {
+ return null;
+ }
+
+ byte[] to = new byte[from.size()];
+ for (int i = 0; i < from.size(); ++i) {
+ to[i] = from.get(i);
+ }
+ return to;
+ }
+
+ private static String statusString(WifiNanStatus status) {
+ if (status == null) {
+ return "status=null";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(status.status).append(" (").append(status.description).append(")");
+ return sb.toString();
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
new file mode 100644
index 0000000..22f1385
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeManager.java
@@ -0,0 +1,162 @@
+/*
+ * 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.aware;
+
+import android.hardware.wifi.V1_0.IWifiNanIface;
+import android.hardware.wifi.V1_0.IfaceType;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.wifi.HalDeviceManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Manages the interface to Wi-Fi Aware HIDL (HAL).
+ */
+class WifiAwareNativeManager {
+ private static final String TAG = "WifiAwareNativeManager";
+ private static final boolean DBG = false;
+
+ // to be used for synchronizing access to any of the WifiAwareNative objects
+ private final Object mLock = new Object();
+
+ private WifiAwareStateManager mWifiAwareStateManager;
+ private HalDeviceManager mHalDeviceManager;
+ private WifiAwareNativeCallback mWifiAwareNativeCallback;
+ private IWifiNanIface mWifiNanIface = null;
+ private InterfaceDestroyedListener mInterfaceDestroyedListener =
+ new InterfaceDestroyedListener();
+ private InterfaceAvailableForRequestListener mInterfaceAvailableForRequestListener =
+ new InterfaceAvailableForRequestListener();
+
+ WifiAwareNativeManager(WifiAwareStateManager awareStateManager,
+ HalDeviceManager halDeviceManager,
+ WifiAwareNativeCallback wifiAwareNativeCallback) {
+ mWifiAwareStateManager = awareStateManager;
+ mHalDeviceManager = halDeviceManager;
+ mWifiAwareNativeCallback = wifiAwareNativeCallback;
+ mHalDeviceManager.registerStatusListener(
+ new HalDeviceManager.ManagerStatusListener() {
+ @Override
+ public void onStatusChanged() {
+ if (DBG) Log.d(TAG, "onStatusChanged");
+ // only care about isStarted (Wi-Fi started) not isReady - since if not
+ // ready then Wi-Fi will also be down.
+ if (mHalDeviceManager.isStarted()) {
+ // 1. no problem registering duplicates - only one will be called
+ // 2. will be called immediately if available
+ mHalDeviceManager.registerInterfaceAvailableForRequestListener(
+ IfaceType.NAN, mInterfaceAvailableForRequestListener, null);
+ } else {
+ awareIsDown();
+ }
+ }
+ }, null);
+ if (mHalDeviceManager.isStarted()) {
+ tryToGetAware();
+ }
+ }
+
+ /* package */ IWifiNanIface getWifiNanIface() {
+ synchronized (mLock) {
+ return mWifiNanIface;
+ }
+ }
+
+ private void tryToGetAware() {
+ synchronized (mLock) {
+ if (DBG) Log.d(TAG, "tryToGetAware: mWifiNanIface=" + mWifiNanIface);
+
+ if (mWifiNanIface != null) {
+ return;
+ }
+ IWifiNanIface iface = mHalDeviceManager.createNanIface(mInterfaceDestroyedListener,
+ null);
+ if (iface == null) {
+ if (DBG) Log.d(TAG, "Was not able to obtain an IWifiNanIface");
+ } else {
+ if (DBG) Log.d(TAG, "Obtained an IWifiNanIface");
+
+ try {
+ WifiStatus status = iface.registerEventCallback(mWifiAwareNativeCallback);
+ if (status.code != WifiStatusCode.SUCCESS) {
+ Log.e(TAG, "IWifiNanIface.registerEventCallback error: " + statusString(
+ status));
+ mHalDeviceManager.removeIface(iface);
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "IWifiNanIface.registerEventCallback exception: " + e);
+ mHalDeviceManager.removeIface(iface);
+ return;
+ }
+ mWifiNanIface = iface;
+ mWifiAwareStateManager.enableUsage();
+ }
+ }
+ }
+
+ private void awareIsDown() {
+ synchronized (mLock) {
+ if (DBG) Log.d(TAG, "awareIsDown: mWifiNanIface=" + mWifiNanIface);
+ if (mWifiNanIface != null) {
+ mWifiNanIface = null;
+ mWifiAwareStateManager.disableUsage();
+ }
+ }
+ }
+
+ private class InterfaceDestroyedListener implements
+ HalDeviceManager.InterfaceDestroyedListener {
+ @Override
+ public void onDestroyed() {
+ if (DBG) Log.d(TAG, "Interface was destroyed");
+ awareIsDown();
+ }
+ }
+
+ private class InterfaceAvailableForRequestListener implements
+ HalDeviceManager.InterfaceAvailableForRequestListener {
+ @Override
+ public void onAvailableForRequest() {
+ if (DBG) Log.d(TAG, "Interface is possibly available");
+ tryToGetAware();
+ }
+ }
+
+ private static String statusString(WifiStatus status) {
+ if (status == null) {
+ return "status=null";
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(status.code).append(" (").append(status.description).append(")");
+ return sb.toString();
+ }
+
+ /**
+ * Dump the internal state of the class.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("WifiAwareNativeManager:");
+ pw.println(" mWifiNanIface: " + mWifiNanIface);
+ mHalDeviceManager.dump(fd, pw, args);
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareRttStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareRttStateManager.java
new file mode 100644
index 0000000..74f34b3
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareRttStateManager.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.aware;
+
+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 Aware and the RTT service. Duplicates some of the functionality
+ * of the RttManager.
+ */
+public class WifiAwareRttStateManager {
+ private static final String TAG = "WifiAwareRttStateMgr";
+
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ private final SparseArray<WifiAwareClientState> 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 AwareRttHandler(looper), messenger);
+ }
+
+ private WifiAwareClientState getAndRemovePendingOperationClient(int rangingId) {
+ WifiAwareClientState client = mPendingOperations.get(rangingId);
+ mPendingOperations.delete(rangingId);
+ return client;
+ }
+
+ /**
+ * Start a ranging operation for the client + peer MAC.
+ */
+ public void startRanging(int rangingId, WifiAwareClientState 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,
+ "Aware 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 AwareRttHandler extends Handler {
+ AwareRttHandler(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
+ WifiAwareClientState 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("WifiAwareRttStateManager:");
+ pw.println(" mPendingOperations: [" + mPendingOperations + "]");
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareService.java b/service/java/com/android/server/wifi/aware/WifiAwareService.java
new file mode 100644
index 0000000..75efa02
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareService.java
@@ -0,0 +1,73 @@
+/*
+ * 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.aware;
+
+import android.content.Context;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import com.android.server.SystemService;
+import com.android.server.wifi.HalDeviceManager;
+import com.android.server.wifi.WifiInjector;
+
+/**
+ * Service implementing Wi-Fi Aware functionality. Delegates actual interface
+ * implementation to WifiAwareServiceImpl.
+ */
+public final class WifiAwareService extends SystemService {
+ private static final String TAG = "WifiAwareService";
+ final WifiAwareServiceImpl mImpl;
+
+ public WifiAwareService(Context context) {
+ super(context);
+ mImpl = new WifiAwareServiceImpl(context);
+ }
+
+ @Override
+ public void onStart() {
+ Log.i(TAG, "Registering " + Context.WIFI_AWARE_SERVICE);
+ publishBinderService(Context.WIFI_AWARE_SERVICE, mImpl);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ WifiInjector wifiInjector = WifiInjector.getInstance();
+ if (wifiInjector == null) {
+ Log.e(TAG, "onBootPhase(PHASE_SYSTEM_SERVICES_READY): NULL injector!");
+ return;
+ }
+
+ HalDeviceManager halDeviceManager = wifiInjector.getHalDeviceManager();
+ halDeviceManager.initialize();
+
+ WifiAwareStateManager wifiAwareStateManager = new WifiAwareStateManager();
+ WifiAwareNativeCallback wifiAwareNativeCallback = new WifiAwareNativeCallback(
+ wifiAwareStateManager);
+ WifiAwareNativeManager wifiAwareNativeManager = new WifiAwareNativeManager(
+ wifiAwareStateManager, halDeviceManager, wifiAwareNativeCallback);
+ WifiAwareNativeApi wifiAwareNativeApi = new WifiAwareNativeApi(wifiAwareNativeManager);
+ wifiAwareStateManager.setNative(wifiAwareNativeApi);
+
+ HandlerThread awareHandlerThread = wifiInjector.getWifiAwareHandlerThread();
+ mImpl.start(awareHandlerThread, wifiAwareStateManager);
+ } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+ mImpl.startLate();
+ }
+ }
+}
+
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
new file mode 100644
index 0000000..fa695cd
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java
@@ -0,0 +1,417 @@
+/*
+ * 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.aware;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.wifi.V1_0.NanStatusType;
+import android.net.wifi.RttManager;
+import android.net.wifi.aware.Characteristics;
+import android.net.wifi.aware.ConfigRequest;
+import android.net.wifi.aware.DiscoverySession;
+import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
+import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.IWifiAwareManager;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.SubscribeConfig;
+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 IWifiAwareManager AIDL interface. Performs validity
+ * (permission and clientID-UID mapping) checks and delegates execution to the
+ * WifiAwareStateManager singleton handler. Limited state to feedback which has to
+ * be provided instantly: client and session IDs.
+ */
+public class WifiAwareServiceImpl extends IWifiAwareManager.Stub {
+ private static final String TAG = "WifiAwareService";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ private Context mContext;
+ private WifiAwareStateManager mStateManager;
+
+ private final Object mLock = new Object();
+ private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId =
+ new SparseArray<>();
+ private int mNextClientId = 1;
+ private int mNextRangingId = 1;
+ private final SparseIntArray mUidByClientId = new SparseIntArray();
+
+ public WifiAwareServiceImpl(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ /**
+ * 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(HandlerThread handlerThread, WifiAwareStateManager awareStateManager) {
+ Log.i(TAG, "Starting Wi-Fi Aware service");
+
+ mStateManager = awareStateManager;
+ mStateManager.start(mContext, handlerThread.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 Aware service");
+
+ mStateManager.startLate();
+ }
+
+ @Override
+ public boolean isUsageEnabled() {
+ enforceAccessPermission();
+
+ return mStateManager.isUsageEnabled();
+ }
+
+ @Override
+ public Characteristics getCharacteristics() {
+ enforceAccessPermission();
+
+ return mStateManager.getCapabilities() == null ? null
+ : mStateManager.getCapabilities().toPublicCharacteristics();
+ }
+
+ @Override
+ public void connect(final IBinder binder, String callingPackage,
+ IWifiAwareEventCallback callback, ConfigRequest configRequest,
+ boolean notifyOnIdentityChanged) {
+ 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 (notifyOnIdentityChanged) {
+ enforceLocationPermission();
+ }
+
+ if (configRequest != null) {
+ enforceConnectivityInternalPermission();
+ } else {
+ configRequest = new ConfigRequest.Builder().build();
+ }
+ configRequest.validate();
+
+ final int uid = getMockableCallingUid();
+ int pid = getCallingPid();
+
+ final int clientId;
+ synchronized (mLock) {
+ clientId = mNextClientId++;
+ }
+
+ if (VDBG) {
+ Log.v(TAG, "connect: uid=" + uid + ", clientId=" + clientId + ", configRequest"
+ + configRequest + ", notifyOnIdentityChanged=" + notifyOnIdentityChanged);
+ }
+
+ IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ if (DBG) Log.d(TAG, "binderDied: clientId=" + clientId);
+ binder.unlinkToDeath(this, 0);
+
+ synchronized (mLock) {
+ mDeathRecipientsByClientId.delete(clientId);
+ mUidByClientId.delete(clientId);
+ }
+
+ mStateManager.disconnect(clientId);
+ }
+ };
+
+ try {
+ binder.linkToDeath(dr, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error on linkToDeath - " + e);
+ try {
+ callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e1) {
+ Log.e(TAG, "Error on onConnectFail()");
+ }
+ return;
+ }
+
+ synchronized (mLock) {
+ mDeathRecipientsByClientId.put(clientId, dr);
+ mUidByClientId.put(clientId, uid);
+ }
+
+ mStateManager.connect(clientId, uid, pid, callingPackage, callback, configRequest,
+ notifyOnIdentityChanged);
+ }
+
+ @Override
+ public void disconnect(int clientId, IBinder binder) {
+ enforceAccessPermission();
+ enforceChangePermission();
+
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) Log.v(TAG, "disconnect: uid=" + uid + ", clientId=" + clientId);
+
+ if (binder == null) {
+ throw new IllegalArgumentException("Binder must not be null");
+ }
+
+ synchronized (mLock) {
+ IBinder.DeathRecipient dr = mDeathRecipientsByClientId.get(clientId);
+ if (dr != null) {
+ binder.unlinkToDeath(dr, 0);
+ mDeathRecipientsByClientId.delete(clientId);
+ }
+ mUidByClientId.delete(clientId);
+ }
+
+ mStateManager.disconnect(clientId);
+ }
+
+ @Override
+ 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,
+ IWifiAwareDiscoverySessionCallback callback) {
+ enforceAccessPermission();
+ enforceChangePermission();
+ enforceLocationPermission();
+
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ if (publishConfig == null) {
+ throw new IllegalArgumentException("PublishConfig must not be null");
+ }
+ publishConfig.assertValid(mStateManager.getCharacteristics());
+
+ 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.assertValid(mStateManager.getCharacteristics());
+
+ 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,
+ IWifiAwareDiscoverySessionCallback callback) {
+ enforceAccessPermission();
+ enforceChangePermission();
+ enforceLocationPermission();
+
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ if (subscribeConfig == null) {
+ throw new IllegalArgumentException("SubscribeConfig must not be null");
+ }
+ subscribeConfig.assertValid(mStateManager.getCharacteristics());
+
+ 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.assertValid(mStateManager.getCharacteristics());
+
+ 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 messageId,
+ int retryCount) {
+ enforceAccessPermission();
+ enforceChangePermission();
+
+ if (retryCount != 0) {
+ enforceConnectivityInternalPermission();
+ }
+
+ if (message != null
+ && message.length > mStateManager.getCharacteristics().getMaxServiceNameLength()) {
+ throw new IllegalArgumentException(
+ "Message length longer than supported by device characteristics");
+ }
+ if (retryCount < 0 || retryCount > DiscoverySession.getMaxSendRetryCount()) {
+ throw new IllegalArgumentException("Invalid 'retryCount' must be non-negative "
+ + "and <= DiscoverySession.MAX_SEND_RETRY_COUNT");
+ }
+
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) {
+ Log.v(TAG,
+ "sendMessage: sessionId=" + sessionId + ", uid=" + uid + ", clientId="
+ + clientId + ", peerId=" + peerId + ", messageId=" + messageId
+ + ", retryCount=" + retryCount);
+ }
+
+ mStateManager.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount);
+ }
+
+ @Override
+ public int startRanging(int clientId, int sessionId, RttManager.ParcelableRttParams params) {
+ enforceAccessPermission();
+ enforceLocationPermission();
+
+ // TODO: b/35676064 restricts access to this API until decide if will open.
+ enforceConnectivityInternalPermission();
+
+ int uid = getMockableCallingUid();
+ enforceClientValidity(uid, clientId);
+ if (VDBG) {
+ Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", "
+ + ", parms=" + Arrays.toString(params.mParams));
+ }
+
+ if (params.mParams.length == 0) {
+ throw new IllegalArgumentException("Empty ranging parameters");
+ }
+
+ int rangingId;
+ synchronized (mLock) {
+ rangingId = mNextRangingId++;
+ }
+ mStateManager.startRanging(clientId, sessionId, params.mParams, rangingId);
+ return rangingId;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump WifiAwareService from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+ pw.println("Wi-Fi Aware Service");
+ 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);
+ }
+
+ private void enforceChangePermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
+ }
+
+ private void enforceLocationPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ TAG);
+ }
+
+ private void enforceConnectivityInternalPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL,
+ TAG);
+ }
+}
diff --git a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
new file mode 100644
index 0000000..a35c786
--- /dev/null
+++ b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java
@@ -0,0 +1,2707 @@
+/*
+ * 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.aware;
+
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.wifi.V1_0.NanStatusType;
+import android.net.wifi.RttManager;
+import android.net.wifi.aware.Characteristics;
+import android.net.wifi.aware.ConfigRequest;
+import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
+import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.WifiAwareManager;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+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.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Manages the state of the Wi-Fi Aware system service.
+ */
+public class WifiAwareStateManager {
+ private static final String TAG = "WifiAwareStateManager";
+ 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";
+
+ /*
+ * 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_AWARE_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[]{WifiAwareStateManager.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_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_SSI_DATA = "ssi_data";
+ private static final String MESSAGE_BUNDLE_KEY_FILTER_DATA = "filter_data";
+ 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_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_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_PID = "pid";
+ private static final String MESSAGE_BUNDLE_KEY_CALLING_PACKAGE = "calling_package";
+ 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 static final String MESSAGE_BUNDLE_KEY_NOTIFY_IDENTITY_CHANGE = "notify_identity_chg";
+ private static final String MESSAGE_BUNDLE_KEY_PMK = "pmk";
+ private static final String MESSAGE_BUNDLE_KEY_PASSPHRASE = "passphrase";
+
+ private WifiAwareNativeApi mWifiAwareNativeApi;
+
+ /*
+ * Asynchronous access with no lock
+ */
+ private volatile boolean mUsageEnabled = false;
+
+ /*
+ * Synchronous access: state is only accessed through the state machine
+ * handler thread: no need to use a lock.
+ */
+ private Context mContext;
+ private volatile Capabilities mCapabilities;
+ private volatile Characteristics mCharacteristics = null;
+ private WifiAwareStateMachine mSm;
+ private WifiAwareRttStateManager mRtt;
+ private WifiAwareDataPathStateManager mDataPathMgr;
+
+ private final SparseArray<WifiAwareClientState> mClients = new SparseArray<>();
+ private ConfigRequest mCurrentAwareConfiguration = null;
+ private boolean mCurrentIdentityNotification = false;
+
+ private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
+ private byte[] mCurrentDiscoveryInterfaceMac = ALL_ZERO_MAC;
+
+ public WifiAwareStateManager() {
+ // empty
+ }
+
+ public void setNative(WifiAwareNativeApi wifiAwareNativeApi) {
+ mWifiAwareNativeApi = wifiAwareNativeApi;
+ }
+
+ /**
+ * 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()");
+
+ mContext = context;
+ mSm = new WifiAwareStateMachine(TAG, looper);
+ mSm.setDbg(DBG);
+ mSm.start();
+
+ mRtt = new WifiAwareRttStateManager();
+ mDataPathMgr = new WifiAwareDataPathStateManager(this);
+ mDataPathMgr.start(mContext, mSm.getHandler().getLooper());
+ }
+
+ /**
+ * Initialize the late-initialization sub-services: depend on other services already existing.
+ */
+ public void startLate() {
+ mRtt.start(mContext, mSm.getHandler().getLooper());
+ }
+
+ /**
+ * Get the client state for the specified ID (or null if none exists).
+ */
+ /* package */ WifiAwareClientState getClient(int clientId) {
+ return mClients.get(clientId);
+ }
+
+ /**
+ * Get the capabilities.
+ */
+ public Capabilities getCapabilities() {
+ return mCapabilities;
+ }
+
+ /**
+ * Get the public characteristics derived from the capabilities. Use lazy initialization.
+ */
+ public Characteristics getCharacteristics() {
+ if (mCharacteristics == null && mCapabilities != null) {
+ mCharacteristics = mCapabilities.toPublicCharacteristics();
+ }
+
+ return mCharacteristics;
+ }
+
+ /*
+ * COMMANDS
+ */
+
+ /**
+ * Place a request for a new client connection on the state machine queue.
+ */
+ public void connect(int clientId, int uid, int pid, String callingPackage,
+ IWifiAwareEventCallback callback, ConfigRequest configRequest,
+ boolean notifyOnIdentityChanged) {
+ 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);
+ msg.getData().putInt(MESSAGE_BUNDLE_KEY_PID, pid);
+ msg.getData().putString(MESSAGE_BUNDLE_KEY_CALLING_PACKAGE, callingPackage);
+ msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_NOTIFY_IDENTITY_CHANGE,
+ notifyOnIdentityChanged);
+ mSm.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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Place a request to start a new publish discovery session on the state
+ * machine queue.
+ */
+ public void publish(int clientId, PublishConfig publishConfig,
+ IWifiAwareDiscoverySessionCallback 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Place a request to start a new subscribe discovery session on the state
+ * machine queue.
+ */
+ public void subscribe(int clientId, SubscribeConfig subscribeConfig,
+ IWifiAwareDiscoverySessionCallback 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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 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_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 Aware. Doesn't actually turn on Aware (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 Aware. Terminates all existing clients with onAwareDown().
+ */
+ public void disableUsage() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_DISABLE_USAGE;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Checks whether Aware usage is enabled (not necessarily that Aware is up right
+ * now) or disabled.
+ *
+ * @return A boolean indicating whether Aware usage is enabled (true) or
+ * disabled (false).
+ */
+ public boolean isUsageEnabled() {
+ return mUsageEnabled;
+ }
+
+ /**
+ * Get the capabilities of the current Aware firmware.
+ */
+ public void queryCapabilities() {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
+ msg.arg1 = COMMAND_TYPE_GET_CAPABILITIES;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Create all Aware 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 Aware 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(WifiAwareNetworkSpecifier networkSpecifier, int peerId,
+ int channelRequestType, int channel, byte[] peer, String interfaceName, byte[] pmk,
+ String passphrase) {
+ 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_PMK, pmk);
+ msg.getData().putString(MESSAGE_BUNDLE_KEY_PASSPHRASE, passphrase);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Command to respond to the data-path request (executed by the responder).
+ */
+ public void respondToDataPathRequest(boolean accept, int ndpId, String interfaceName,
+ byte[] pmk, String passphrase) {
+ 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().putByteArray(MESSAGE_BUNDLE_KEY_PMK, pmk);
+ msg.getData().putString(MESSAGE_BUNDLE_KEY_PASSPHRASE, passphrase);
+ 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);
+ }
+
+ /**
+ * Aware 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 Aware stack.
+ */
+ public void onCapabilitiesUpdateResponse(short transactionId,
+ Capabilities capabilities) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
+ msg.arg1 = RESPONSE_TYPE_ON_CAPABILITIES_UPDATED;
+ msg.arg2 = transactionId;
+ msg.obj = capabilities;
+ mSm.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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Response from firmware to initiateDataPathSetup(...). 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);
+ }
+
+ /**
+ * Response from firmware to initiateDataPathSetup(...).
+ * 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);
+ }
+
+ /**
+ * Response from firmware to
+ * {@link #respondToDataPathRequest(boolean, int, String, byte[], 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /*
+ * NOTIFICATIONS
+ */
+
+ /**
+ * 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;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * 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;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * 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, byte[] matchFilter) {
+ 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().putByteArray(MESSAGE_BUNDLE_KEY_FILTER_DATA, matchFilter);
+ mSm.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);
+ }
+
+ /**
+ * 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) {
+ 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);
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * Place a callback request on the state machine queue: Aware is going down.
+ */
+ public void onAwareDownNotification(int reason) {
+ Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
+ msg.arg1 = NOTIFICATION_TYPE_AWARE_DOWN;
+ msg.arg2 = reason;
+ mSm.sendMessage(msg);
+ }
+
+ /**
+ * 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) {
+ 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);
+ 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) {
+ 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);
+ 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 WifiAwareStateMachine 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 AWARE_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 AWARE_WAIT_FOR_DP_CONFIRM_TIMEOUT = 5_000;
+ private final Map<WifiAwareNetworkSpecifier, WakeupMessage>
+ mDataPathConfirmTimeoutMessages = new ArrayMap<>();
+
+ WifiAwareStateMachine(String name, Looper looper) {
+ super(name, looper);
+
+ addState(mDefaultState);
+ /* --> */ addState(mWaitState, mDefaultState);
+ /* --> */ addState(mWaitForResponseState, mDefaultState);
+
+ setInitialState(mWaitState);
+ }
+
+ public void onAwareDownCleanupSendQueueState() {
+ 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: {
+ WifiAwareNetworkSpecifier networkSpecifier =
+ (WifiAwareNetworkSpecifier) 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;
+ }
+ }
+
+ private class WaitState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ if (VDBG) {
+ Log.v(TAG, getName() + msg.toString());
+ }
+
+ 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 */
+ }
+
+ return NOT_HANDLED;
+ }
+ }
+
+ private class WaitForResponseState extends State {
+ private static final long AWARE_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() + AWARE_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 NOTIFICATION_TYPE_CLUSTER_CHANGE: {
+ int flag = msg.arg2;
+ byte[] clusterId = (byte[]) msg.obj;
+
+ onClusterChangeLocal(flag, clusterId);
+ break;
+ }
+ 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);
+ byte[] matchFilter = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_FILTER_DATA);
+
+ onMatchLocal(pubSubId, requestorInstanceId, peerMac, serviceSpecificInfo,
+ matchFilter);
+ break;
+ }
+ 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);
+
+ onMessageReceivedLocal(pubSubId, requestorInstanceId, peerMac, message);
+ break;
+ }
+ case NOTIFICATION_TYPE_AWARE_DOWN: {
+ int reason = msg.arg2;
+
+ /*
+ * TODO: b/28615938. Use reason code to determine whether or not need clean-up
+ * local state (only needed if AWARE_DOWN is due to internal firmware reason,
+ * e.g. concurrency, rather than due to a requested shutdown).
+ */
+
+ onAwareDownLocal();
+
+ 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 == NanStatusType.NO_OTA_ACK) {
+ 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: {
+ WifiAwareNetworkSpecifier networkSpecifier = mDataPathMgr.onDataPathRequest(
+ msg.arg2, msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS),
+ (int) msg.obj);
+
+ 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() + AWARE_WAIT_FOR_DP_CONFIRM_TIMEOUT);
+ }
+
+ break;
+ }
+ case NOTIFICATION_TYPE_ON_DATA_PATH_CONFIRM: {
+ WifiAwareNetworkSpecifier 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));
+
+ 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.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;
+ IWifiAwareEventCallback callback = (IWifiAwareEventCallback) msg.obj;
+ ConfigRequest configRequest = (ConfigRequest) msg.getData()
+ .getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
+ int uid = msg.getData().getInt(MESSAGE_BUNDLE_KEY_UID);
+ int pid = msg.getData().getInt(MESSAGE_BUNDLE_KEY_PID);
+ String callingPackage = msg.getData().getString(
+ MESSAGE_BUNDLE_KEY_CALLING_PACKAGE);
+ boolean notifyIdentityChange = msg.getData().getBoolean(
+ MESSAGE_BUNDLE_KEY_NOTIFY_IDENTITY_CHANGE);
+
+ waitForResponse = connectLocal(mCurrentTransactionId, clientId, uid, pid,
+ callingPackage, callback, configRequest, notifyIdentityChange);
+ 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;
+ IWifiAwareDiscoverySessionCallback callback =
+ (IWifiAwareDiscoverySessionCallback) 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;
+ IWifiAwareDiscoverySessionCallback callback =
+ (IWifiAwareDiscoverySessionCallback) 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);
+
+ msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_SENT_MESSAGE, sendMessage);
+
+ waitForResponse = sendFollowonMessageLocal(mCurrentTransactionId, clientId,
+ sessionId, peerId, message, 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 = mWifiAwareNativeApi.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 = mWifiAwareNativeApi.createAwareNetworkInterface(
+ mCurrentTransactionId, (String) msg.obj);
+ break;
+ case COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE:
+ waitForResponse = mWifiAwareNativeApi.deleteAwareNetworkInterface(
+ mCurrentTransactionId, (String) msg.obj);
+ break;
+ case COMMAND_TYPE_INITIATE_DATA_PATH_SETUP: {
+ Bundle data = msg.getData();
+
+ WifiAwareNetworkSpecifier networkSpecifier =
+ (WifiAwareNetworkSpecifier) 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[] pmk = data.getByteArray(MESSAGE_BUNDLE_KEY_PMK);
+ String passphrase = data.getString(MESSAGE_BUNDLE_KEY_PASSPHRASE);
+
+ waitForResponse = initiateDataPathSetupLocal(mCurrentTransactionId,
+ networkSpecifier, peerId, channelRequestType, channel, peer,
+ interfaceName, pmk, passphrase);
+
+ 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() + AWARE_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);
+ byte[] pmk = data.getByteArray(MESSAGE_BUNDLE_KEY_PMK);
+ String passphrase = data.getString(MESSAGE_BUNDLE_KEY_PASSPHRASE);
+
+ waitForResponse = respondToDataPathRequestLocal(mCurrentTransactionId, accept,
+ ndpId, interfaceName, pmk, passphrase);
+
+ 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!");
+ }
+ int reason = (Integer) msg.obj;
+ if (reason == NanStatusType.FOLLOWUP_TX_QUEUE_FULL) {
+ 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");
+ }
+ } else {
+ Message sentMessage = mCurrentCommand.getData().getParcelable(
+ MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
+ onMessageSendFailLocal(sentMessage, NanStatusType.INTERNAL_FAILURE);
+ if (!mSendQueueBlocked) {
+ transmitNextMessage();
+ }
+ }
+ break;
+ }
+ case RESPONSE_TYPE_ON_CAPABILITIES_UPDATED: {
+ onCapabilitiesUpdatedResponseLocal((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, NanStatusType.INTERNAL_FAILURE);
+ break;
+ }
+ case COMMAND_TYPE_DISCONNECT: {
+ /*
+ * Will only get here on DISCONNECT if was downgrading. The
+ * callback will do a NOP - but should still call it.
+ */
+ onConfigFailedLocal(mCurrentCommand, NanStatusType.INTERNAL_FAILURE);
+ break;
+ }
+ case COMMAND_TYPE_TERMINATE_SESSION: {
+ Log.wtf(TAG, "processTimeout: TERMINATE_SESSION - shouldn't be waiting!");
+ break;
+ }
+ case COMMAND_TYPE_PUBLISH: {
+ onSessionConfigFailLocal(mCurrentCommand, true, NanStatusType.INTERNAL_FAILURE);
+ break;
+ }
+ case COMMAND_TYPE_UPDATE_PUBLISH: {
+ onSessionConfigFailLocal(mCurrentCommand, true, NanStatusType.INTERNAL_FAILURE);
+ break;
+ }
+ case COMMAND_TYPE_SUBSCRIBE: {
+ onSessionConfigFailLocal(mCurrentCommand, false,
+ NanStatusType.INTERNAL_FAILURE);
+ break;
+ }
+ case COMMAND_TYPE_UPDATE_SUBSCRIBE: {
+ onSessionConfigFailLocal(mCurrentCommand, false,
+ NanStatusType.INTERNAL_FAILURE);
+ 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, NanStatusType.INTERNAL_FAILURE);
+ 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)
+ + AWARE_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 + AWARE_SEND_MESSAGE_TIMEOUT <= currentTime) {
+ if (VDBG) {
+ Log.v(TAG, "processSendMessageTimeout: expiring - transactionId="
+ + transactionId + ", message=" + message
+ + ", due to messageEnqueueTime=" + messageEnqueueTime
+ + ", currentTime=" + currentTime);
+ }
+ onMessageSendFailLocal(message, NanStatusType.INTERNAL_FAILURE);
+ it.remove();
+ first = false;
+ } else {
+ break;
+ }
+ }
+ updateSendMessageTimeout();
+ mSendQueueBlocked = false;
+ transmitNextMessage();
+ }
+
+ @Override
+ protected String getLogRecString(Message msg) {
+ StringBuilder sb = new StringBuilder(WifiAwareStateManager.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("WifiAwareStateMachine:");
+ 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 sendAwareStateChangedBroadcast(boolean enabled) {
+ if (VDBG) {
+ Log.v(TAG, "sendAwareStateChangedBroadcast: enabled=" + enabled);
+ }
+ final Intent intent = new Intent(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ /*
+ * COMMANDS
+ */
+
+ private boolean connectLocal(short transactionId, int clientId, int uid, int pid,
+ String callingPackage, IWifiAwareEventCallback callback, ConfigRequest configRequest,
+ boolean notifyIdentityChange) {
+ if (VDBG) {
+ Log.v(TAG, "connectLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+ + ", uid=" + uid + ", pid=" + pid + ", callingPackage=" + callingPackage
+ + ", callback=" + callback + ", configRequest=" + configRequest
+ + ", notifyIdentityChange=" + notifyIdentityChange);
+ }
+
+ if (!mUsageEnabled) {
+ Log.w(TAG, "connect(): called with mUsageEnabled=false");
+ return false;
+ }
+
+ if (mClients.get(clientId) != null) {
+ Log.e(TAG, "connectLocal: entry already exists for clientId=" + clientId);
+ }
+
+ if (VDBG) {
+ Log.v(TAG, "mCurrentAwareConfiguration=" + mCurrentAwareConfiguration
+ + ", mCurrentIdentityNotification=" + mCurrentIdentityNotification);
+ }
+
+ ConfigRequest merged = mergeConfigRequests(configRequest);
+ if (merged == null) {
+ Log.e(TAG, "connectLocal: requested configRequest=" + configRequest
+ + ", incompatible with current configurations");
+ try {
+ callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
+ }
+ return false;
+ } else if (VDBG) {
+ Log.v(TAG, "connectLocal: merged=" + merged);
+ }
+
+ if (mCurrentAwareConfiguration != null && mCurrentAwareConfiguration.equals(merged)
+ && mCurrentIdentityNotification == notifyIdentityChange) {
+ try {
+ callback.onConnectSuccess(clientId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "connectLocal onConnectSuccess(): RemoteException (FYI): " + e);
+ }
+ WifiAwareClientState client = new WifiAwareClientState(mContext, clientId, uid, pid,
+ callingPackage, callback, configRequest, notifyIdentityChange);
+ client.onInterfaceAddressChange(mCurrentDiscoveryInterfaceMac);
+ mClients.append(clientId, client);
+ return false;
+ }
+ boolean notificationRequired =
+ doesAnyClientNeedIdentityChangeNotifications() || notifyIdentityChange;
+
+ boolean success = mWifiAwareNativeApi.enableAndConfigure(transactionId, merged,
+ notificationRequired, mCurrentAwareConfiguration == null);
+ if (!success) {
+ try {
+ callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
+ }
+ }
+
+ return success;
+ }
+
+ private boolean disconnectLocal(short transactionId, int clientId) {
+ if (VDBG) {
+ Log.v(TAG,
+ "disconnectLocal(): transactionId=" + transactionId + ", clientId=" + clientId);
+ }
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "disconnectLocal: no entry for clientId=" + clientId);
+ return false;
+ }
+ mClients.delete(clientId);
+ client.destroy();
+
+ if (mClients.size() == 0) {
+ mCurrentAwareConfiguration = null;
+ mWifiAwareNativeApi.disable((short) 0);
+ return false;
+ }
+
+ ConfigRequest merged = mergeConfigRequests(null);
+ if (merged == null) {
+ Log.wtf(TAG, "disconnectLocal: got an incompatible merge on remaining configs!?");
+ return false;
+ }
+ boolean notificationReqs = doesAnyClientNeedIdentityChangeNotifications();
+ if (merged.equals(mCurrentAwareConfiguration)
+ && mCurrentIdentityNotification == notificationReqs) {
+ return false;
+ }
+
+ return mWifiAwareNativeApi.enableAndConfigure(transactionId, merged, notificationReqs,
+ false);
+ }
+
+ private void terminateSessionLocal(int clientId, int sessionId) {
+ if (VDBG) {
+ Log.v(TAG,
+ "terminateSessionLocal(): clientId=" + clientId + ", sessionId=" + sessionId);
+ }
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "terminateSession: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ client.terminateSession(sessionId);
+ }
+
+ private boolean publishLocal(short transactionId, int clientId, PublishConfig publishConfig,
+ IWifiAwareDiscoverySessionCallback callback) {
+ if (VDBG) {
+ Log.v(TAG, "publishLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+ + ", publishConfig=" + publishConfig + ", callback=" + callback);
+ }
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "publishLocal: no client exists for clientId=" + clientId);
+ return false;
+ }
+
+ boolean success = mWifiAwareNativeApi.publish(transactionId, 0, publishConfig);
+ if (!success) {
+ try {
+ callback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.w(TAG, "publishLocal onSessionConfigFail(): RemoteException (FYI): " + e);
+ }
+ }
+
+ return success;
+ }
+
+ private boolean updatePublishLocal(short transactionId, int clientId, int sessionId,
+ PublishConfig publishConfig) {
+ if (VDBG) {
+ Log.v(TAG, "updatePublishLocal(): transactionId=" + transactionId + ", clientId="
+ + clientId + ", sessionId=" + sessionId + ", publishConfig=" + publishConfig);
+ }
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "updatePublishLocal: no client exists for clientId=" + clientId);
+ return false;
+ }
+
+ WifiAwareDiscoverySessionState 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, IWifiAwareDiscoverySessionCallback callback) {
+ if (VDBG) {
+ Log.v(TAG, "subscribeLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+ + ", subscribeConfig=" + subscribeConfig + ", callback=" + callback);
+ }
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "subscribeLocal: no client exists for clientId=" + clientId);
+ return false;
+ }
+
+ boolean success = mWifiAwareNativeApi.subscribe(transactionId, 0, subscribeConfig);
+ if (!success) {
+ try {
+ callback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
+ } catch (RemoteException e) {
+ Log.w(TAG, "subscribeLocal onSessionConfigFail(): RemoteException (FYI): " + e);
+ }
+ }
+
+ return success;
+ }
+
+ 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);
+ }
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "updateSubscribeLocal: no client exists for clientId=" + clientId);
+ return false;
+ }
+
+ WifiAwareDiscoverySessionState 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 messageId) {
+ if (VDBG) {
+ Log.v(TAG,
+ "sendFollowonMessageLocal(): transactionId=" + transactionId + ", clientId="
+ + clientId + ", sessionId=" + sessionId + ", peerId=" + peerId
+ + ", messageId=" + messageId);
+ }
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "sendFollowonMessageLocal: no client exists for clientId=" + clientId);
+ return false;
+ }
+
+ WifiAwareDiscoverySessionState 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, messageId);
+ }
+
+ private void enableUsageLocal() {
+ if (VDBG) Log.v(TAG, "enableUsageLocal: mUsageEnabled=" + mUsageEnabled);
+
+ if (mUsageEnabled) {
+ return;
+ }
+
+ mUsageEnabled = true;
+ queryCapabilities();
+ createAllDataPathInterfaces();
+ sendAwareStateChangedBroadcast(true);
+ }
+
+ private void disableUsageLocal() {
+ if (VDBG) Log.v(TAG, "disableUsageLocal: mUsageEnabled=" + mUsageEnabled);
+
+ if (!mUsageEnabled) {
+ return;
+ }
+
+ onAwareDownLocal();
+ deleteAllDataPathInterfaces();
+
+ mUsageEnabled = false;
+ mWifiAwareNativeApi.disable((short) 0);
+
+ sendAwareStateChangedBroadcast(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);
+ }
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "startRangingLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiAwareDiscoverySessionState 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 = "";
+ }
+ }
+
+ mRtt.startRanging(rangingId, client, params);
+ }
+
+ private boolean initiateDataPathSetupLocal(short transactionId,
+ WifiAwareNetworkSpecifier networkSpecifier, int peerId, int channelRequestType,
+ int channel, byte[] peer, String interfaceName, byte[] pmk, String passphrase) {
+ if (VDBG) {
+ Log.v(TAG,
+ "initiateDataPathSetupLocal(): transactionId=" + transactionId
+ + ", networkSpecifier=" + networkSpecifier + ", peerId=" + peerId
+ + ", channelRequestType=" + channelRequestType + ", channel=" + channel
+ + ", peer=" + String.valueOf(HexEncoding.encode(peer))
+ + ", interfaceName=" + interfaceName + ", pmk=" + pmk
+ + ", passphrase=" + passphrase);
+ }
+
+ boolean success = mWifiAwareNativeApi.initiateDataPath(transactionId, peerId,
+ channelRequestType, channel, peer, interfaceName, pmk, passphrase, mCapabilities);
+ if (!success) {
+ mDataPathMgr.onDataPathInitiateFail(networkSpecifier, NanStatusType.INTERNAL_FAILURE);
+ }
+
+ return success;
+ }
+
+ private boolean respondToDataPathRequestLocal(short transactionId, boolean accept,
+ int ndpId, String interfaceName, byte[] pmk, String passphrase) {
+ if (VDBG) {
+ Log.v(TAG,
+ "respondToDataPathRequestLocal(): transactionId=" + transactionId + ", accept="
+ + accept + ", ndpId=" + ndpId + ", interfaceName=" + interfaceName
+ + ", pmk=" + pmk + ", passphrase=" + passphrase);
+ }
+
+ boolean success = mWifiAwareNativeApi.respondToDataPathRequest(transactionId, accept, ndpId,
+ interfaceName, pmk, passphrase, mCapabilities);
+ if (!success) {
+ mDataPathMgr.onRespondToDataPathRequest(ndpId, false);
+ }
+ return success;
+ }
+
+ private boolean endDataPathLocal(short transactionId, int ndpId) {
+ if (VDBG) {
+ Log.v(TAG,
+ "endDataPathLocal: transactionId=" + transactionId + ", ndpId=" + ndpId);
+ }
+
+ return mWifiAwareNativeApi.endDataPath(transactionId, ndpId);
+ }
+
+ /*
+ * RESPONSES
+ */
+
+ private void onConfigCompletedLocal(Message completedCommand) {
+ if (VDBG) {
+ Log.v(TAG, "onConfigCompleted: completedCommand=" + completedCommand);
+ }
+
+ if (completedCommand.arg1 == COMMAND_TYPE_CONNECT) {
+ Bundle data = completedCommand.getData();
+
+ int clientId = completedCommand.arg2;
+ IWifiAwareEventCallback callback = (IWifiAwareEventCallback) completedCommand.obj;
+ ConfigRequest configRequest = (ConfigRequest) data
+ .getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
+ int uid = data.getInt(MESSAGE_BUNDLE_KEY_UID);
+ int pid = data.getInt(MESSAGE_BUNDLE_KEY_PID);
+ boolean notifyIdentityChange = data.getBoolean(
+ MESSAGE_BUNDLE_KEY_NOTIFY_IDENTITY_CHANGE);
+ String callingPackage = data.getString(MESSAGE_BUNDLE_KEY_CALLING_PACKAGE);
+
+ WifiAwareClientState client = new WifiAwareClientState(mContext, clientId, uid, pid,
+ callingPackage, callback, configRequest, notifyIdentityChange);
+ mClients.put(clientId, client);
+ try {
+ callback.onConnectSuccess(clientId);
+ } 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;
+ }
+
+ mCurrentAwareConfiguration = mergeConfigRequests(null);
+ if (mCurrentAwareConfiguration == null) {
+ Log.wtf(TAG, "onConfigCompletedLocal: got a null merged configuration after config!?");
+ }
+ mCurrentIdentityNotification = doesAnyClientNeedIdentityChangeNotifications();
+ }
+
+ private void onConfigFailedLocal(Message failedCommand, int reason) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onConfigFailedLocal: failedCommand=" + failedCommand + ", reason=" + reason);
+ }
+
+ if (failedCommand.arg1 == COMMAND_TYPE_CONNECT) {
+ IWifiAwareEventCallback callback = (IWifiAwareEventCallback) 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;
+ IWifiAwareDiscoverySessionCallback callback =
+ (IWifiAwareDiscoverySessionCallback) completedCommand.obj;
+
+ WifiAwareClientState 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;
+ }
+
+ WifiAwareDiscoverySessionState session = new WifiAwareDiscoverySessionState(
+ mWifiAwareNativeApi, 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);
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG,
+ "onSessionConfigSuccessLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiAwareDiscoverySessionState 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) {
+ IWifiAwareDiscoverySessionCallback callback =
+ (IWifiAwareDiscoverySessionCallback) 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);
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "onSessionConfigFailLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiAwareDiscoverySessionState 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);
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "onMessageSendSuccessLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiAwareDiscoverySessionState 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);
+
+ WifiAwareClientState client = mClients.get(clientId);
+ if (client == null) {
+ Log.e(TAG, "onMessageSendFailLocal: no client exists for clientId=" + clientId);
+ return;
+ }
+
+ WifiAwareDiscoverySessionState 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(Capabilities capabilities) {
+ if (VDBG) {
+ Log.v(TAG, "onCapabilitiesUpdatedResponseLocal: capabilites=" + capabilities);
+ }
+
+ mCapabilities = capabilities;
+ mCharacteristics = null;
+ }
+
+ private void onCreateDataPathInterfaceResponseLocal(Message command, boolean success,
+ int reasonOnFailure) {
+ if (VDBG) {
+ Log.v(TAG, "onCreateDataPathInterfaceResponseLocal: command=" + command + ", success="
+ + success + ", reasonOnFailure=" + reasonOnFailure);
+ }
+
+ 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 onDeleteDataPathInterfaceResponseLocal(Message command, boolean success,
+ int reasonOnFailure) {
+ if (VDBG) {
+ Log.v(TAG, "onDeleteDataPathInterfaceResponseLocal: command=" + command + ", success="
+ + success + ", reasonOnFailure=" + reasonOnFailure);
+ }
+
+ 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 onInitiateDataPathResponseSuccessLocal(Message command, int ndpId) {
+ if (VDBG) {
+ Log.v(TAG, "onInitiateDataPathResponseSuccessLocal: command=" + command + ", ndpId="
+ + ndpId);
+ }
+
+ mDataPathMgr.onDataPathInitiateSuccess((WifiAwareNetworkSpecifier) command.obj, ndpId);
+ }
+
+ private void onInitiateDataPathResponseFailLocal(Message command, int reason) {
+ if (VDBG) {
+ Log.v(TAG, "onInitiateDataPathResponseFailLocal: command=" + command + ", reason="
+ + reason);
+ }
+
+ mDataPathMgr.onDataPathInitiateFail((WifiAwareNetworkSpecifier) command.obj, reason);
+ }
+
+ private void onRespondToDataPathSetupRequestResponseLocal(Message command, boolean success,
+ int reasonOnFailure) {
+ if (VDBG) {
+ Log.v(TAG, "onRespondToDataPathSetupRequestResponseLocal: command=" + command
+ + ", success=" + success + ", reasonOnFailure=" + reasonOnFailure);
+ }
+
+ mDataPathMgr.onRespondToDataPathRequest(command.arg2, success);
+ }
+
+ 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)));
+ }
+
+ mCurrentDiscoveryInterfaceMac = mac;
+
+ for (int i = 0; i < mClients.size(); ++i) {
+ WifiAwareClientState client = mClients.valueAt(i);
+ client.onInterfaceAddressChange(mac);
+ }
+ }
+
+ private void onClusterChangeLocal(int flag, byte[] clusterId) {
+ if (VDBG) {
+ Log.v(TAG, "onClusterChange: flag=" + flag + ", clusterId="
+ + String.valueOf(HexEncoding.encode(clusterId)));
+ }
+
+ for (int i = 0; i < mClients.size(); ++i) {
+ WifiAwareClientState client = mClients.valueAt(i);
+ client.onClusterChange(flag, clusterId, mCurrentDiscoveryInterfaceMac);
+ }
+ }
+
+ private void onMatchLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
+ byte[] serviceSpecificInfo, byte[] matchFilter) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onMatch: pubSubId=" + pubSubId + ", requestorInstanceId=" + requestorInstanceId
+ + ", peerDiscoveryMac=" + String.valueOf(HexEncoding.encode(peerMac))
+ + ", serviceSpecificInfo=" + Arrays.toString(serviceSpecificInfo)
+ + ", matchFilter=" + Arrays.toString(matchFilter));
+ }
+
+ Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data =
+ getClientSessionForPubSubId(pubSubId);
+ if (data == null) {
+ Log.e(TAG, "onMatch: no session found for pubSubId=" + pubSubId);
+ return;
+ }
+
+ data.second.onMatch(requestorInstanceId, peerMac, serviceSpecificInfo, matchFilter);
+ }
+
+ private void onSessionTerminatedLocal(int pubSubId, boolean isPublish, int reason) {
+ if (VDBG) {
+ Log.v(TAG, "onSessionTerminatedLocal: pubSubId=" + pubSubId + ", isPublish=" + isPublish
+ + ", reason=" + reason);
+ }
+
+ Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> 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) {
+ if (VDBG) {
+ Log.v(TAG,
+ "onMessageReceivedLocal: pubSubId=" + pubSubId + ", requestorInstanceId="
+ + requestorInstanceId + ", peerDiscoveryMac="
+ + String.valueOf(HexEncoding.encode(peerMac)));
+ }
+
+ Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data =
+ getClientSessionForPubSubId(pubSubId);
+ if (data == null) {
+ Log.e(TAG, "onMessageReceivedLocal: no session found for pubSubId=" + pubSubId);
+ return;
+ }
+
+ data.second.onMessageReceived(requestorInstanceId, peerMac, message);
+ }
+
+ private void onAwareDownLocal() {
+ if (VDBG) {
+ Log.v(TAG, "onAwareDown");
+ }
+
+ mClients.clear();
+ mCurrentAwareConfiguration = null;
+ mSm.onAwareDownCleanupSendQueueState();
+ mDataPathMgr.onAwareDownCleanupDataPaths();
+ mCurrentDiscoveryInterfaceMac = ALL_ZERO_MAC;
+ }
+
+ /*
+ * Utilities
+ */
+
+ private Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> getClientSessionForPubSubId(
+ int pubSubId) {
+ for (int i = 0; i < mClients.size(); ++i) {
+ WifiAwareClientState client = mClients.valueAt(i);
+ WifiAwareDiscoverySessionState session = client.getAwareSessionStateForPubSubId(
+ pubSubId);
+ if (session != null) {
+ return new Pair<>(client, session);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Merge all the existing client configurations with the (optional) input configuration request.
+ * If the configurations are "incompatible" (rules in comment below) return a 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;
+ }
+
+ // TODO: continue working on merge algorithm:
+ // - if any request 5g: enable
+ // - maximal master preference
+ // - cluster range: must be identical
+ // - if any request identity change: enable
+ // - discovery window: minimum value if specified, 0 (disable) is considered an infinity
+ boolean support5gBand = false;
+ int masterPreference = 0;
+ boolean clusterIdValid = false;
+ int clusterLow = 0;
+ int clusterHigh = ConfigRequest.CLUSTER_ID_MAX;
+ int[] discoveryWindowInterval =
+ {ConfigRequest.DW_INTERVAL_NOT_INIT, ConfigRequest.DW_INTERVAL_NOT_INIT};
+ if (configRequest != null) {
+ support5gBand = configRequest.mSupport5gBand;
+ masterPreference = configRequest.mMasterPreference;
+ clusterIdValid = true;
+ clusterLow = configRequest.mClusterLow;
+ clusterHigh = configRequest.mClusterHigh;
+ discoveryWindowInterval = configRequest.mDiscoveryWindowInterval;
+ }
+ for (int i = 0; i < mClients.size(); ++i) {
+ ConfigRequest cr = mClients.valueAt(i).getConfigRequest();
+
+ // any request turns on 5G
+ if (cr.mSupport5gBand) {
+ support5gBand = true;
+ }
+
+ // maximal master preference
+ masterPreference = Math.max(masterPreference, cr.mMasterPreference);
+
+ // cluster range must be the same across all config requests
+ if (!clusterIdValid) {
+ clusterIdValid = true;
+ clusterLow = cr.mClusterLow;
+ clusterHigh = cr.mClusterHigh;
+ } else {
+ if (clusterLow != cr.mClusterLow) return null;
+ if (clusterHigh != cr.mClusterHigh) return null;
+ }
+
+ for (int band = ConfigRequest.NAN_BAND_24GHZ; band <= ConfigRequest.NAN_BAND_5GHZ;
+ ++band) {
+ if (discoveryWindowInterval[band] == ConfigRequest.DW_INTERVAL_NOT_INIT) {
+ discoveryWindowInterval[band] = cr.mDiscoveryWindowInterval[band];
+ } else if (cr.mDiscoveryWindowInterval[band]
+ == ConfigRequest.DW_INTERVAL_NOT_INIT) {
+ // do nothing: keep my values
+ } else if (discoveryWindowInterval[band] == ConfigRequest.DW_DISABLE) {
+ discoveryWindowInterval[band] = cr.mDiscoveryWindowInterval[band];
+ } else if (cr.mDiscoveryWindowInterval[band] == ConfigRequest.DW_DISABLE) {
+ // do nothing: keep my values
+ } else {
+ discoveryWindowInterval[band] = Math.min(discoveryWindowInterval[band],
+ cr.mDiscoveryWindowInterval[band]);
+ }
+ }
+ }
+ ConfigRequest.Builder builder = new ConfigRequest.Builder().setSupport5gBand(support5gBand)
+ .setMasterPreference(masterPreference).setClusterLow(clusterLow)
+ .setClusterHigh(clusterHigh);
+ for (int band = ConfigRequest.NAN_BAND_24GHZ; band <= ConfigRequest.NAN_BAND_5GHZ; ++band) {
+ if (discoveryWindowInterval[band] != ConfigRequest.DW_INTERVAL_NOT_INIT) {
+ builder.setDiscoveryWindowInterval(band, discoveryWindowInterval[band]);
+ }
+ }
+ return builder.build();
+ }
+
+ private boolean doesAnyClientNeedIdentityChangeNotifications() {
+ for (int i = 0; i < mClients.size(); ++i) {
+ if (mClients.valueAt(i).getNotifyIdentityChange()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ 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("AwareStateManager:");
+ pw.println(" mClients: [" + mClients + "]");
+ pw.println(" mUsageEnabled: " + mUsageEnabled);
+ pw.println(" mCapabilities: [" + mCapabilities + "]");
+ pw.println(" mCurrentAwareConfiguration: " + mCurrentAwareConfiguration);
+ pw.println(" mCurrentIdentityNotification: " + mCurrentIdentityNotification);
+ 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);
+ mWifiAwareNativeApi.dump(fd, pw, args);
+ }
+}
diff --git a/service/java/com/android/server/wifi/configparse/ConfigBuilder.java b/service/java/com/android/server/wifi/configparse/ConfigBuilder.java
deleted file mode 100644
index ad0165e..0000000
--- a/service/java/com/android/server/wifi/configparse/ConfigBuilder.java
+++ /dev/null
@@ -1,404 +0,0 @@
-package com.android.server.wifi.configparse;
-
-import android.content.Context;
-import android.net.Uri;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiEnterpriseConfig;
-import android.provider.DocumentsContract;
-import android.util.Base64;
-import android.util.Log;
-
-import com.android.server.wifi.IMSIParameter;
-import com.android.server.wifi.anqp.eap.AuthParam;
-import com.android.server.wifi.anqp.eap.EAP;
-import com.android.server.wifi.anqp.eap.EAPMethod;
-import com.android.server.wifi.anqp.eap.NonEAPInnerAuth;
-import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager;
-import com.android.server.wifi.hotspot2.pps.Credential;
-import com.android.server.wifi.hotspot2.pps.HomeSP;
-
-import org.xml.sax.SAXException;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.LineNumberReader;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.security.MessageDigest;
-import java.security.PrivateKey;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-
-public class ConfigBuilder {
- public static final String WifiConfigType = "application/x-wifi-config";
- private static final String ProfileTag = "application/x-passpoint-profile";
- private static final String KeyTag = "application/x-pkcs12";
- private static final String CATag = "application/x-x509-ca-cert";
-
- private static final String X509 = "X.509";
-
- private static final String TAG = "WCFG";
-
- public static WifiConfiguration buildConfig(String uriString, byte[] data, Context context)
- throws IOException, GeneralSecurityException, SAXException {
- Log.d(TAG, "Content: " + (data != null ? data.length : -1));
-
- byte[] b64 = Base64.decode(new String(data, StandardCharsets.ISO_8859_1), Base64.DEFAULT);
- Log.d(TAG, "Decoded: " + b64.length + " bytes.");
-
- MIMEContainer mimeContainer = new
- MIMEContainer(new LineNumberReader(
- new InputStreamReader(new ByteArrayInputStream(b64), StandardCharsets.ISO_8859_1)),
- null);
- if (!mimeContainer.isBase64()) {
- throw new IOException("Encoding for " +
- mimeContainer.getContentType() + " is not base64");
- }
- MIMEContainer inner;
- if (mimeContainer.getContentType().equals(WifiConfigType)) {
- byte[] wrappedContent = Base64.decode(mimeContainer.getText(), Base64.DEFAULT);
- Log.d(TAG, "Building container from '" +
- new String(wrappedContent, StandardCharsets.ISO_8859_1) + "'");
- inner = new MIMEContainer(new LineNumberReader(
- new InputStreamReader(new ByteArrayInputStream(wrappedContent),
- StandardCharsets.ISO_8859_1)), null);
- }
- else {
- inner = mimeContainer;
- }
- return parse(inner);
- }
-
- private static WifiConfiguration parse(MIMEContainer root)
- throws IOException, GeneralSecurityException, SAXException {
-
- if (root.getMimeContainers() == null) {
- throw new IOException("Malformed MIME content: not multipart");
- }
-
- String moText = null;
- X509Certificate caCert = null;
- PrivateKey clientKey = null;
- List<X509Certificate> clientChain = null;
-
- for (MIMEContainer subContainer : root.getMimeContainers()) {
- Log.d(TAG, " + Content Type: " + subContainer.getContentType());
- switch (subContainer.getContentType()) {
- case ProfileTag:
- if (subContainer.isBase64()) {
- byte[] octets = Base64.decode(subContainer.getText(), Base64.DEFAULT);
- moText = new String(octets, StandardCharsets.UTF_8);
- } else {
- moText = subContainer.getText();
- }
- Log.d(TAG, "OMA: " + moText);
- break;
- case CATag: {
- if (!subContainer.isBase64()) {
- throw new IOException("Can't read non base64 encoded cert");
- }
-
- byte[] octets = Base64.decode(subContainer.getText(), Base64.DEFAULT);
- CertificateFactory factory = CertificateFactory.getInstance(X509);
- caCert = (X509Certificate) factory.generateCertificate(
- new ByteArrayInputStream(octets));
- Log.d(TAG, "Cert subject " + caCert.getSubjectX500Principal());
- Log.d(TAG, "Full Cert: " + caCert);
- break;
- }
- case KeyTag: {
- if (!subContainer.isBase64()) {
- throw new IOException("Can't read non base64 encoded key");
- }
-
- byte[] octets = Base64.decode(subContainer.getText(), Base64.DEFAULT);
-
- KeyStore ks = KeyStore.getInstance("PKCS12");
- ByteArrayInputStream in = new ByteArrayInputStream(octets);
- ks.load(in, new char[0]);
- in.close();
- Log.d(TAG, "---- Start PKCS12 info " + octets.length + ", size " + ks.size());
- Enumeration<String> aliases = ks.aliases();
- while (aliases.hasMoreElements()) {
- String alias = aliases.nextElement();
- clientKey = (PrivateKey) ks.getKey(alias, null);
- Log.d(TAG, "Key: " + clientKey.getFormat());
- Certificate[] chain = ks.getCertificateChain(alias);
- if (chain != null) {
- clientChain = new ArrayList<>();
- for (Certificate certificate : chain) {
- if (!(certificate instanceof X509Certificate)) {
- Log.w(TAG, "Element in cert chain is not an X509Certificate: " +
- certificate.getClass());
- }
- clientChain.add((X509Certificate) certificate);
- }
- Log.d(TAG, "Chain: " + clientChain.size());
- }
- }
- Log.d(TAG, "---- End PKCS12 info.");
- break;
- }
- }
- }
-
- if (moText == null) {
- throw new IOException("Missing profile");
- }
-
- HomeSP homeSP = PasspointManagementObjectManager.buildSP(moText);
-
- return buildConfig(homeSP, caCert, clientChain, clientKey);
- }
-
- private static WifiConfiguration buildConfig(HomeSP homeSP, X509Certificate caCert,
- List<X509Certificate> clientChain, PrivateKey key)
- throws IOException, GeneralSecurityException {
-
- WifiConfiguration config;
-
- EAP.EAPMethodID eapMethodID = homeSP.getCredential().getEAPMethod().getEAPMethodID();
- switch (eapMethodID) {
- case EAP_TTLS:
- if (key != null || clientChain != null) {
- Log.w(TAG, "Client cert and/or key unnecessarily included with EAP-TTLS "+
- "profile");
- }
- config = buildTTLSConfig(homeSP, caCert);
- break;
- case EAP_TLS:
- config = buildTLSConfig(homeSP, clientChain, key, caCert);
- break;
- case EAP_AKA:
- case EAP_AKAPrim:
- case EAP_SIM:
- if (key != null || clientChain != null || caCert != null) {
- Log.i(TAG, "Client/CA cert and/or key unnecessarily included with " +
- eapMethodID + " profile");
- }
- config = buildSIMConfig(homeSP);
- break;
- default:
- throw new IOException("Unsupported EAP Method: " + eapMethodID);
- }
-
- return config;
- }
-
- // Retain for debugging purposes
- /*
- private static void xIterateCerts(KeyStore ks, X509Certificate caCert)
- throws GeneralSecurityException {
- Enumeration<String> aliases = ks.aliases();
- while (aliases.hasMoreElements()) {
- String alias = aliases.nextElement();
- Certificate cert = ks.getCertificate(alias);
- Log.d("HS2J", "Checking " + alias);
- if (cert instanceof X509Certificate) {
- X509Certificate x509Certificate = (X509Certificate) cert;
- boolean sm = x509Certificate.getSubjectX500Principal().equals(
- caCert.getSubjectX500Principal());
- boolean eq = false;
- if (sm) {
- eq = Arrays.equals(x509Certificate.getEncoded(), caCert.getEncoded());
- }
- Log.d("HS2J", "Subject: " + x509Certificate.getSubjectX500Principal() +
- ": " + sm + "/" + eq);
- }
- }
- }
- */
-
- private static void setAnonymousIdentityToNaiRealm(
- WifiConfiguration config, Credential credential) {
- /**
- * Set WPA supplicant's anonymous identity field to a string containing the NAI realm, so
- * that this value will be sent to the EAP server as part of the EAP-Response/ Identity
- * packet. WPA supplicant will reset this field after using it for the EAP-Response/Identity
- * packet, and revert to using the (real) identity field for subsequent transactions that
- * request an identity (e.g. in EAP-TTLS).
- *
- * This NAI realm value (the portion of the identity after the '@') is used to tell the
- * AAA server which AAA/H to forward packets to. The hardcoded username, "anonymous", is a
- * placeholder that is not used--it is set to this value by convention. See Section 5.1 of
- * RFC3748 for more details.
- *
- * NOTE: we do not set this value for EAP-SIM/AKA/AKA', since the EAP server expects the
- * EAP-Response/Identity packet to contain an actual, IMSI-based identity, in order to
- * identify the device.
- */
- config.enterpriseConfig.setAnonymousIdentity("anonymous@" + credential.getRealm());
- }
-
- private static WifiConfiguration buildTTLSConfig(HomeSP homeSP, X509Certificate caCert)
- throws IOException {
- Credential credential = homeSP.getCredential();
-
- if (credential.getUserName() == null || credential.getPassword() == null) {
- throw new IOException("EAP-TTLS provisioned without user name or password");
- }
-
- EAPMethod eapMethod = credential.getEAPMethod();
-
- AuthParam authParam = eapMethod.getAuthParam();
- if (authParam == null ||
- authParam.getAuthInfoID() != EAP.AuthInfoID.NonEAPInnerAuthType) {
- throw new IOException("Bad auth parameter for EAP-TTLS: " + authParam);
- }
-
- WifiConfiguration config = buildBaseConfiguration(homeSP);
- NonEAPInnerAuth ttlsParam = (NonEAPInnerAuth) authParam;
- WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
- enterpriseConfig.setPhase2Method(remapInnerMethod(ttlsParam.getType()));
- enterpriseConfig.setIdentity(credential.getUserName());
- enterpriseConfig.setPassword(credential.getPassword());
- enterpriseConfig.setCaCertificate(caCert);
-
- setAnonymousIdentityToNaiRealm(config, credential);
-
- return config;
- }
-
- private static WifiConfiguration buildTLSConfig(HomeSP homeSP,
- List<X509Certificate> clientChain,
- PrivateKey clientKey,
- X509Certificate caCert)
- throws IOException, GeneralSecurityException {
-
- Credential credential = homeSP.getCredential();
-
- X509Certificate clientCertificate = null;
-
- if (clientKey == null || clientChain == null) {
- throw new IOException("No key and/or cert passed for EAP-TLS");
- }
- if (credential.getCertType() != Credential.CertType.x509v3) {
- throw new IOException("Invalid certificate type for TLS: " +
- credential.getCertType());
- }
-
- byte[] reference = credential.getFingerPrint();
- MessageDigest digester = MessageDigest.getInstance("SHA-256");
- for (X509Certificate certificate : clientChain) {
- digester.reset();
- byte[] fingerprint = digester.digest(certificate.getEncoded());
- if (Arrays.equals(reference, fingerprint)) {
- clientCertificate = certificate;
- break;
- }
- }
- if (clientCertificate == null) {
- throw new IOException("No certificate in chain matches supplied fingerprint");
- }
-
- String alias = Base64.encodeToString(reference, Base64.DEFAULT);
-
- WifiConfiguration config = buildBaseConfiguration(homeSP);
- WifiEnterpriseConfig enterpriseConfig = config.enterpriseConfig;
- enterpriseConfig.setClientCertificateAlias(alias);
- enterpriseConfig.setClientKeyEntry(clientKey, clientCertificate);
- enterpriseConfig.setCaCertificate(caCert);
-
- setAnonymousIdentityToNaiRealm(config, credential);
-
- return config;
- }
-
- private static WifiConfiguration buildSIMConfig(HomeSP homeSP)
- throws IOException {
-
- Credential credential = homeSP.getCredential();
- IMSIParameter credImsi = credential.getImsi();
-
- /*
- * Uncomment to enforce strict IMSI matching with currently installed SIM cards.
- *
- TelephonyManager tm = TelephonyManager.from(context);
- SubscriptionManager sub = SubscriptionManager.from(context);
- boolean match = false;
-
- for (int subId : sub.getActiveSubscriptionIdList()) {
- String imsi = tm.getSubscriberId(subId);
- if (credImsi.matches(imsi)) {
- match = true;
- break;
- }
- }
- if (!match) {
- throw new IOException("Supplied IMSI does not match any SIM card");
- }
- */
-
- WifiConfiguration config = buildBaseConfiguration(homeSP);
- config.enterpriseConfig.setPlmn(credImsi.toString());
- return config;
- }
-
- private static WifiConfiguration buildBaseConfiguration(HomeSP homeSP) throws IOException {
- EAP.EAPMethodID eapMethodID = homeSP.getCredential().getEAPMethod().getEAPMethodID();
-
- WifiConfiguration config = new WifiConfiguration();
-
- config.FQDN = homeSP.getFQDN();
-
- HashSet<Long> roamingConsortiumIds = homeSP.getRoamingConsortiums();
- config.roamingConsortiumIds = new long[roamingConsortiumIds.size()];
- int i = 0;
- for (long id : roamingConsortiumIds) {
- config.roamingConsortiumIds[i] = id;
- i++;
- }
- config.providerFriendlyName = homeSP.getFriendlyName();
-
- config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
- config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
-
- WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
- enterpriseConfig.setEapMethod(remapEAPMethod(eapMethodID));
- enterpriseConfig.setRealm(homeSP.getCredential().getRealm());
- config.enterpriseConfig = enterpriseConfig;
- // The framework based config builder only ever builds r1 configs:
- config.updateIdentifier = null;
-
- return config;
- }
-
- private static int remapEAPMethod(EAP.EAPMethodID eapMethodID) throws IOException {
- switch (eapMethodID) {
- case EAP_TTLS:
- return WifiEnterpriseConfig.Eap.TTLS;
- case EAP_TLS:
- return WifiEnterpriseConfig.Eap.TLS;
- case EAP_SIM:
- return WifiEnterpriseConfig.Eap.SIM;
- case EAP_AKA:
- return WifiEnterpriseConfig.Eap.AKA;
- case EAP_AKAPrim:
- return WifiEnterpriseConfig.Eap.AKA_PRIME;
- default:
- throw new IOException("Bad EAP method: " + eapMethodID);
- }
- }
-
- private static int remapInnerMethod(NonEAPInnerAuth.NonEAPType type) throws IOException {
- switch (type) {
- case PAP:
- return WifiEnterpriseConfig.Phase2.PAP;
- case MSCHAP:
- return WifiEnterpriseConfig.Phase2.MSCHAP;
- case MSCHAPv2:
- return WifiEnterpriseConfig.Phase2.MSCHAPV2;
- case CHAP:
- default:
- throw new IOException("Inner method " + type + " not supported");
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/configparse/MIMEContainer.java b/service/java/com/android/server/wifi/configparse/MIMEContainer.java
deleted file mode 100644
index 10ad456..0000000
--- a/service/java/com/android/server/wifi/configparse/MIMEContainer.java
+++ /dev/null
@@ -1,345 +0,0 @@
-package com.android.server.wifi.configparse;
-
-import android.util.Log;
-
-import com.android.server.wifi.hotspot2.Utils;
-
-import java.io.IOException;
-import java.io.LineNumberReader;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class MIMEContainer {
- private static final String Type = "Content-Type";
- private static final String Encoding = "Content-Transfer-Encoding";
-
- private static final String Boundary = "boundary=";
- private static final String CharsetTag = "charset=";
-
- private final boolean mLast;
- private final List<MIMEContainer> mMimeContainers;
- private final String mText;
-
- private final boolean mMixed;
- private final boolean mBase64;
- private final Charset mCharset;
- private final String mContentType;
-
- /**
- * Parse nested MIME content
- * @param in A reader to read MIME data from; Note that the charset should be ISO-8859-1 to
- * ensure transparent octet to character mapping. This is because the content will
- * be re-encoded using the correct charset once it is discovered.
- * @param boundary A boundary string for the MIME section that this container is in.
- * Pass null for the top level object.
- * @throws java.io.IOException
- */
- public MIMEContainer(LineNumberReader in, String boundary) throws IOException {
- Map<String,List<String>> headers = parseHeader(in);
-
- List<String> type = headers.get(Type);
- if (type == null || type.isEmpty()) {
- throw new IOException("Missing " + Type + " @ " + in.getLineNumber());
- }
-
- boolean multiPart = false;
- boolean mixed = false;
- String subBoundary = null;
- Charset charset = StandardCharsets.ISO_8859_1;
-
- mContentType = type.get(0);
-
- if (mContentType.startsWith("multipart/")) {
- multiPart = true;
-
- for (String attribute : type) {
- if (attribute.startsWith(Boundary)) {
- subBoundary = Utils.unquote(attribute.substring(Boundary.length()));
- }
- }
-
- if (mContentType.endsWith("/mixed")) {
- mixed = true;
- }
- }
- else if (mContentType.startsWith("text/")) {
- for (String attribute : type) {
- if (attribute.startsWith(CharsetTag)) {
- charset = Charset.forName(attribute.substring(CharsetTag.length()));
- }
- }
- }
-
- mMixed = mixed;
- mCharset = charset;
-
- if (multiPart && subBoundary != null) {
- for (;;) {
- String line = in.readLine();
- if (line == null) {
- throw new IOException("Unexpected EOF before first boundary @ " +
- in.getLineNumber());
- }
- if (line.startsWith("--") && line.length() == subBoundary.length() + 2 &&
- line.regionMatches(2, subBoundary, 0, subBoundary.length())) {
- break;
- }
- }
-
- mMimeContainers = new ArrayList<>();
- for (;;) {
- MIMEContainer container = new MIMEContainer(in, subBoundary);
- mMimeContainers.add(container);
- if (container.isLast()) {
- break;
- }
- }
- }
- else {
- mMimeContainers = null;
- }
-
- List<String> encoding = headers.get(Encoding);
- boolean quoted = false;
- boolean base64 = false;
- if (encoding != null) {
- for (String text : encoding) {
- if (text.equalsIgnoreCase("quoted-printable")) {
- quoted = true;
- break;
- }
- else if (text.equalsIgnoreCase("base64")) {
- base64 = true;
- break;
- }
- }
- }
- mBase64 = base64;
-
- Log.d(Utils.hs2LogTag(getClass()),
- String.format("%s MIME container, boundary '%s', type '%s', encoding %s",
- multiPart ? "multipart" : "plain", boundary, mContentType, encoding));
-
- AtomicBoolean eof = new AtomicBoolean();
- mText = recode(getBody(in, boundary, quoted, eof), charset);
- mLast = eof.get();
- }
-
- public List<MIMEContainer> getMimeContainers() {
- return mMimeContainers;
- }
-
- public String getText() {
- return mText;
- }
-
- public boolean isMixed() {
- return mMixed;
- }
-
- public boolean isBase64() {
- return mBase64;
- }
-
- public String getContentType() {
- return mContentType;
- }
-
- private boolean isLast() {
- return mLast;
- }
-
- private void toString(StringBuilder sb, int nesting) {
- char[] indent = new char[nesting*4];
- Arrays.fill(indent, ' ');
- if (mBase64) {
- sb.append("base64, type ").append(mContentType).append('\n');
- }
- else if (mMimeContainers != null) {
- sb.append(indent).append("multipart/").append((mMixed ? "mixed" : "other" )).append('\n');
- }
- else {
- sb.append(indent).append(
- String.format("%s, type %s",
- mCharset,
- mContentType)
- ).append('\n');
- }
-
- if (mMimeContainers != null) {
- for (MIMEContainer mimeContainer : mMimeContainers) {
- mimeContainer.toString(sb, nesting + 1);
- }
- }
- sb.append(indent).append("Text: ");
- if (mText.length() < 100000) {
- sb.append("'").append(mText).append("'\n");
- }
- else {
- sb.append(mText.length()).append(" chars\n");
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- toString(sb, 0);
- return sb.toString();
- }
-
- private static Map<String,List<String>> parseHeader(LineNumberReader in) throws IOException {
-
- StringBuilder value = null;
- String header = null;
-
- Map<String,List<String>> headers = new HashMap<>();
-
- for (;;) {
- String line = in.readLine();
- if ( line == null ) {
- throw new IOException("Missing body @ " + in.getLineNumber());
- }
- else if (line.length() == 0) {
- break;
- }
-
- if (line.charAt(0) <= ' ') {
- if (value == null) {
- throw new IOException("Illegal blank prefix in header line '" + line + "' @ " + in.getLineNumber());
- }
- value.append(' ').append(line.trim());
- continue;
- }
-
- int nameEnd = line.indexOf(':');
- if (nameEnd < 0) {
- throw new IOException("Bad header line: '" + line + "' @ " + in.getLineNumber());
- }
-
- if (header != null) {
- String[] values = value.toString().split(";");
- List<String> valueList = new ArrayList<>(values.length);
- for (String segment : values) {
- valueList.add(segment.trim());
- }
- headers.put(header, valueList);
- //System.out.println("Header '" + header + "' = " + valueList);
- }
-
- header = line.substring(0, nameEnd);
- value = new StringBuilder();
- value.append(line.substring(nameEnd+1).trim());
- }
-
- if (header != null) {
- String[] values = value.toString().split(";");
- List<String> valueList = new ArrayList<>(values.length);
- for (String segment : values) {
- valueList.add(segment.trim());
- }
- headers.put(header, valueList);
- //System.out.println("Header '" + header + "' = " + valueList);
- }
-
- return headers;
- }
-
- private static String getBody(LineNumberReader in, String boundary, boolean quoted, AtomicBoolean eof)
- throws IOException {
-
- StringBuilder text = new StringBuilder();
- for (;;) {
- String line = in.readLine();
- if (line == null) {
- if (boundary != null) {
- throw new IOException("Unexpected EOF file in body @ " + in.getLineNumber());
- }
- else {
- return text.toString();
- }
- }
- Boolean end = boundaryCheck(line, boundary);
- if (end != null) {
- eof.set(end);
- //System.out.println("Boundary " + boundary + ": " + end);
- return text.toString();
- }
-
- if (quoted) {
- if (line.endsWith("=")) {
- text.append(unescape(line.substring(line.length() - 1), in.getLineNumber()));
- }
- else {
- text.append(unescape(line, in.getLineNumber()));
- }
- }
- else {
- text.append(line);
- }
- }
- }
-
- private static String recode(String s, Charset charset) {
- if (charset.equals(StandardCharsets.ISO_8859_1) || charset.equals(StandardCharsets.US_ASCII)) {
- return s;
- }
-
- byte[] octets = s.getBytes(StandardCharsets.ISO_8859_1);
- return new String(octets, charset);
- }
-
- private static Boolean boundaryCheck(String line, String boundary) {
- if (line.startsWith("--") && line.regionMatches(2, boundary, 0, boundary.length())) {
- if (line.length() == boundary.length() + 2) {
- return Boolean.FALSE;
- }
- else if (line.length() == boundary.length() + 4 && line.endsWith("--") ) {
- return Boolean.TRUE;
- }
- }
- return null;
- }
-
- private static String unescape(String text, int line) throws IOException {
- StringBuilder sb = new StringBuilder();
- for (int n = 0; n < text.length(); n++) {
- char ch = text.charAt(n);
- if (ch > 127) {
- throw new IOException("Bad codepoint " + (int)ch + " in quoted printable @ " + line);
- }
- if (ch == '=' && n < text.length() - 2) {
- int h1 = fromStrictHex(text.charAt(n+1));
- int h2 = fromStrictHex(text.charAt(n+2));
- if (h1 >= 0 && h2 >= 0) {
- sb.append((char)((h1 << 4) | h2));
- n += 2;
- }
- else {
- sb.append(ch);
- }
- }
- else {
- sb.append(ch);
- }
- }
- return sb.toString();
- }
-
- private static int fromStrictHex(char ch) {
- if (ch >= '0' && ch <= '9') {
- return ch - '0';
- }
- else if (ch >= 'A' && ch <= 'F') {
- return ch - 'A' + 10;
- }
- else {
- return -1;
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPData.java b/service/java/com/android/server/wifi/hotspot2/ANQPData.java
index 164ea20..6dbbe8a 100644
--- a/service/java/com/android/server/wifi/hotspot2/ANQPData.java
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPData.java
@@ -1,156 +1,79 @@
+/*
+ * 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.hotspot2;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.Clock;
-import com.android.server.wifi.anqp.ANQPElement;
-import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
+/**
+ * Class for maintaining ANQP elements and managing the lifetime of the elements.
+ */
public class ANQPData {
/**
- * The regular cache time for entries with a non-zero domain id.
+ * Entry lifetime.
*/
- private static final long ANQP_QUALIFIED_CACHE_TIMEOUT = 3600000L;
- /**
- * The cache time for entries with a zero domain id. The zero domain id indicates that ANQP
- * data from the AP may change at any time, thus a relatively short cache time is given to
- * such data, but still long enough to avoid excessive querying.
- */
- private static final long ANQP_UNQUALIFIED_CACHE_TIMEOUT = 300000L;
- /**
- * This is the hold off time for pending queries, i.e. the time during which subsequent queries
- * are squelched.
- */
- private static final long ANQP_HOLDOFF_TIME = 10000L;
+ @VisibleForTesting
+ public static final long DATA_LIFETIME_MILLISECONDS = 3600000L;
- /**
- * Max value for the retry counter for unanswered queries. This limits the maximum time-out to
- * ANQP_HOLDOFF_TIME * 2^MAX_RETRY. With current values this results in 640s.
- */
- private static final int MAX_RETRY = 6;
-
- private final NetworkDetail mNetwork;
- private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements;
- private final long mCtime;
- private final long mExpiry;
- private final int mRetry;
private final Clock mClock;
+ private final Map<Constants.ANQPElementType, ANQPElement> mANQPElements;
+ private final long mExpiryTime;
- public ANQPData(Clock clock, NetworkDetail network,
- Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
-
+ public ANQPData(Clock clock, Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
mClock = clock;
- mNetwork = network;
- mANQPElements = anqpElements != null ? new HashMap<>(anqpElements) : null;
- mCtime = mClock.currentTimeMillis();
- mRetry = 0;
- if (anqpElements == null) {
- mExpiry = mCtime + ANQP_HOLDOFF_TIME;
+ mANQPElements = new HashMap<>();
+ if (anqpElements != null) {
+ mANQPElements.putAll(anqpElements);
}
- else if (network.getAnqpDomainID() == 0) {
- mExpiry = mCtime + ANQP_UNQUALIFIED_CACHE_TIMEOUT;
- }
- else {
- mExpiry = mCtime + ANQP_QUALIFIED_CACHE_TIMEOUT;
- }
+ mExpiryTime = mClock.getElapsedSinceBootMillis() + DATA_LIFETIME_MILLISECONDS;
}
- public ANQPData(Clock clock, NetworkDetail network, ANQPData existing) {
- mClock = clock;
- mNetwork = network;
- mANQPElements = null;
- mCtime = mClock.currentTimeMillis();
- if (existing == null) {
- mRetry = 0;
- mExpiry = mCtime + ANQP_HOLDOFF_TIME;
- }
- else {
- mRetry = Math.max(existing.getRetry() + 1, MAX_RETRY);
- mExpiry = ANQP_HOLDOFF_TIME * (1<<mRetry);
- }
- }
-
- public List<Constants.ANQPElementType> disjoint(List<Constants.ANQPElementType> querySet) {
- if (mANQPElements == null) {
- // Ignore the query set for pending responses, it has minimal probability to happen
- // and a new query will be reissued on the next round anyway.
- return null;
- }
- else {
- List<Constants.ANQPElementType> additions = new ArrayList<>();
- for (Constants.ANQPElementType element : querySet) {
- if (!mANQPElements.containsKey(element)) {
- additions.add(element);
- }
- }
- return additions.isEmpty() ? null : additions;
- }
- }
-
- public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() {
+ /**
+ * Return the ANQP elements.
+ *
+ * @return Map of ANQP elements
+ */
+ public Map<Constants.ANQPElementType, ANQPElement> getElements() {
return Collections.unmodifiableMap(mANQPElements);
}
- public NetworkDetail getNetwork() {
- return mNetwork;
- }
-
- public boolean expired() {
- return expired(mClock.currentTimeMillis());
- }
-
+ /**
+ * Check if this entry is expired at the specified time.
+ *
+ * @param at The time to check for
+ * @return true if it is expired at the given time
+ */
public boolean expired(long at) {
- return mExpiry <= at;
- }
-
- protected boolean hasData() {
- return mANQPElements != null;
- }
-
- protected void merge(Map<Constants.ANQPElementType, ANQPElement> data) {
- if (data != null) {
- mANQPElements.putAll(data);
- }
- }
-
- protected boolean isValid(NetworkDetail nwk) {
- return mANQPElements != null &&
- nwk.getAnqpDomainID() == mNetwork.getAnqpDomainID() &&
- mExpiry > mClock.currentTimeMillis();
- }
-
- private int getRetry() {
- return mRetry;
- }
-
- public String toString(boolean brief) {
- StringBuilder sb = new StringBuilder();
- sb.append(mNetwork.toKeyString()).append(", domid ").append(mNetwork.getAnqpDomainID());
- if (mANQPElements == null) {
- sb.append(", unresolved, ");
- }
- else {
- sb.append(", ").append(mANQPElements.size()).append(" elements, ");
- }
- long now = mClock.currentTimeMillis();
- sb.append(Utils.toHMS(now-mCtime)).append(" old, expires in ").
- append(Utils.toHMS(mExpiry-now)).append(' ');
- if (brief) {
- sb.append(expired(now) ? 'x' : '-');
- sb.append(mANQPElements == null ? 'u' : '-');
- }
- else if (mANQPElements != null) {
- sb.append(" data=").append(mANQPElements);
- }
- return sb.toString();
+ return mExpiryTime <= at;
}
@Override
public String toString() {
- return toString(true);
+ StringBuilder sb = new StringBuilder();
+ sb.append(mANQPElements.size()).append(" elements, ");
+ long now = mClock.getElapsedSinceBootMillis();
+ sb.append(" expires in ").append(Utils.toHMS(mExpiryTime - now)).append(' ');
+ sb.append(expired(now) ? 'x' : '-');
+ return sb.toString();
}
}
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java
new file mode 100644
index 0000000..6495346
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java
@@ -0,0 +1,257 @@
+/*
+ * 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.hotspot2;
+
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.hotspot2.anqp.CellularNetwork;
+import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmData;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmElement;
+import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement;
+import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
+import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
+import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility class for providing matching functions against ANQP elements.
+ */
+public class ANQPMatcher {
+ /**
+ * Match the domain names in the ANQP element against the provider's FQDN and SIM credential.
+ * The Domain Name ANQP element might contain domains for 3GPP network (e.g.
+ * wlan.mnc*.mcc*.3gppnetwork.org), so we should match that against the provider's SIM
+ * credential if one is provided.
+ *
+ * @param element The Domain Name ANQP element
+ * @param fqdn The FQDN to compare against
+ * @param imsiParam The IMSI parameter of the provider
+ * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's
+ * IMSI parameter
+ * @return true if a match is found
+ */
+ public static boolean matchDomainName(DomainNameElement element, String fqdn,
+ IMSIParameter imsiParam, List<String> simImsiList) {
+ if (element == null) {
+ return false;
+ }
+
+ for (String domain : element.getDomains()) {
+ if (DomainMatcher.arg2SubdomainOfArg1(fqdn, domain)) {
+ return true;
+ }
+
+ // Try to retrieve the MCC-MNC string from the domain (for 3GPP network domain) and
+ // match against the provider's SIM credential.
+ if (matchMccMnc(Utils.getMccMnc(Utils.splitDomain(domain)), imsiParam, simImsiList)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Match the roaming consortium OIs in the ANQP element against the roaming consortium OIs
+ * of a provider.
+ *
+ * @param element The Roaming Consortium ANQP element
+ * @param providerOIs The roaming consortium OIs of the provider
+ * @return true if a match is found
+ */
+ public static boolean matchRoamingConsortium(RoamingConsortiumElement element,
+ long[] providerOIs) {
+ if (element == null) {
+ return false;
+ }
+ if (providerOIs == null) {
+ return false;
+ }
+ List<Long> rcOIs = element.getOIs();
+ for (long oi : providerOIs) {
+ if (rcOIs.contains(oi)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Match the NAI realm in the ANQP element against the realm and authentication method of
+ * a provider.
+ *
+ * @param element The NAI Realm ANQP element
+ * @param realm The realm of the provider's credential
+ * @param eapMethodID The EAP Method ID of the provider's credential
+ * @param authParam The authentication parameter of the provider's credential
+ * @return an integer indicating the match status
+ */
+ public static int matchNAIRealm(NAIRealmElement element, String realm, int eapMethodID,
+ AuthParam authParam) {
+ if (element == null || element.getRealmDataList().isEmpty()) {
+ return AuthMatch.INDETERMINATE;
+ }
+
+ int bestMatch = AuthMatch.NONE;
+ for (NAIRealmData realmData : element.getRealmDataList()) {
+ int match = matchNAIRealmData(realmData, realm, eapMethodID, authParam);
+ if (match > bestMatch) {
+ bestMatch = match;
+ if (bestMatch == AuthMatch.EXACT) {
+ break;
+ }
+ }
+ }
+ return bestMatch;
+ }
+
+ /**
+ * Match the 3GPP Network in the ANQP element against the SIM credential of a provider.
+ *
+ * @param element 3GPP Network ANQP element
+ * @param imsiParam The IMSI parameter of the provider's SIM credential
+ * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's
+ * IMSI parameter
+ * @return true if a matched is found
+ */
+ public static boolean matchThreeGPPNetwork(ThreeGPPNetworkElement element,
+ IMSIParameter imsiParam, List<String> simImsiList) {
+ if (element == null) {
+ return false;
+ }
+ for (CellularNetwork network : element.getNetworks()) {
+ if (matchCellularNetwork(network, imsiParam, simImsiList)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Match the given NAI Realm data against the realm and authentication method of a provider.
+ *
+ * @param realmData The NAI Realm data
+ * @param realm The realm of the provider's credential
+ * @param eapMethodID The EAP Method ID of the provider's credential
+ * @param authParam The authentication parameter of the provider's credential
+ * @return an integer indicating the match status
+ */
+ private static int matchNAIRealmData(NAIRealmData realmData, String realm, int eapMethodID,
+ AuthParam authParam) {
+ // Check for realm domain name match.
+ int realmMatch = AuthMatch.NONE;
+ for (String realmStr : realmData.getRealms()) {
+ if (DomainMatcher.arg2SubdomainOfArg1(realm, realmStr)) {
+ realmMatch = AuthMatch.REALM;
+ break;
+ }
+ }
+
+ if (realmMatch == AuthMatch.NONE || realmData.getEAPMethods().isEmpty()) {
+ return realmMatch;
+ }
+
+ // Check for EAP method match.
+ int eapMethodMatch = AuthMatch.NONE;
+ for (EAPMethod eapMethod : realmData.getEAPMethods()) {
+ eapMethodMatch = matchEAPMethod(eapMethod, eapMethodID, authParam);
+ if (eapMethodMatch != AuthMatch.NONE) {
+ break;
+ }
+ }
+
+ if (eapMethodMatch == AuthMatch.NONE) {
+ return AuthMatch.NONE;
+ }
+ return realmMatch | eapMethodMatch;
+ }
+
+ /**
+ * Match the given EAPMethod against the authentication method of a provider.
+ *
+ * @param method The EAP Method
+ * @param eapMethodID The EAP Method ID of the provider's credential
+ * @param authParam The authentication parameter of the provider's credential
+ * @return an integer indicating the match status
+ */
+ private static int matchEAPMethod(EAPMethod method, int eapMethodID, AuthParam authParam) {
+ if (method.getEAPMethodID() != eapMethodID) {
+ return AuthMatch.NONE;
+ }
+ // Check for authentication parameter match.
+ if (authParam != null) {
+ Map<Integer, Set<AuthParam>> authParams = method.getAuthParams();
+ Set<AuthParam> paramSet = authParams.get(authParam.getAuthTypeID());
+ if (paramSet == null || !paramSet.contains(authParam)) {
+ return AuthMatch.NONE;
+ }
+ return AuthMatch.METHOD_PARAM;
+ }
+ return AuthMatch.METHOD;
+ }
+
+ /**
+ * Match a cellular network information in the 3GPP Network ANQP element against the SIM
+ * credential of a provider.
+ *
+ * @param network The cellular network that contained list of PLMNs
+ * @param imsiParam IMSI parameter of the provider
+ * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's
+ * IMSI parameter
+ * @return true if a match is found
+ */
+ private static boolean matchCellularNetwork(CellularNetwork network, IMSIParameter imsiParam,
+ List<String> simImsiList) {
+ for (String plmn : network.getPlmns()) {
+ if (matchMccMnc(plmn, imsiParam, simImsiList)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Match a MCC-MNC against the SIM credential of a provider.
+ *
+ * @param mccMnc The string containing MCC-MNC
+ * @param imsiParam The IMSI parameter of the provider
+ * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's
+ * IMSI parameter
+ * @return true if a match is found
+ */
+ private static boolean matchMccMnc(String mccMnc, IMSIParameter imsiParam,
+ List<String> simImsiList) {
+ if (imsiParam == null || simImsiList == null) {
+ return false;
+ }
+ // Match against the IMSI parameter in the provider.
+ if (!imsiParam.matchesMccMnc(mccMnc)) {
+ return false;
+ }
+ // Additional check for verifying the match with IMSIs from the SIM cards, since the IMSI
+ // parameter might not contain the full 6-digit MCC MNC (e.g. IMSI parameter is an IMSI
+ // prefix that contained less than 6-digit of numbers "12345*").
+ for (String imsi : simImsiList) {
+ if (imsi.startsWith(mccMnc)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java b/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.java
new file mode 100644
index 0000000..aaaedb3
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPNetworkKey.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.hotspot2;
+
+import android.text.TextUtils;
+
+/**
+ * Unique key for identifying APs that will contain the same ANQP information.
+ *
+ * APs in the same ESS (SSID or HESSID) with the same ANQP domain ID will have the same ANQP
+ * information. Thus, those APs will be keyed by the ESS identifier (SSID or HESSID) and the
+ * ANQP domain ID.
+ *
+ * APs without ANQP domain ID set will assumed to have unique ANQP information. Thus, those
+ * APs will be keyed by SSID and BSSID.
+ */
+public class ANQPNetworkKey {
+ private final String mSSID;
+ private final long mBSSID;
+ private final long mHESSID;
+ private final int mAnqpDomainID;
+
+ public ANQPNetworkKey(String ssid, long bssid, long hessid, int anqpDomainID) {
+ mSSID = ssid;
+ mBSSID = bssid;
+ mHESSID = hessid;
+ mAnqpDomainID = anqpDomainID;
+ }
+
+ /**
+ * Build an ANQP network key suitable for the granularity of the key space as follows:
+ *
+ * HESSID domainID Key content Rationale
+ * -------- ----------- ----------- --------------------
+ * n/a zero SSID/BSSID Domain ID indicates unique AP info
+ * not set set SSID/domainID Standard definition of an ESS
+ * set set HESSID/domainID The ESS is defined by the HESSID
+ *
+ * @param ssid The SSID of the AP
+ * @param bssid The BSSID of the AP
+ * @param hessid The HESSID of the AP
+ * @param anqpDomainId The ANQP Domain ID of the AP
+ * @return {@link ANQPNetworkKey}
+ */
+ public static ANQPNetworkKey buildKey(String ssid, long bssid, long hessid, int anqpDomainId) {
+ if (anqpDomainId == 0) {
+ return new ANQPNetworkKey(ssid, bssid, 0, 0);
+ } else if (hessid != 0L) {
+ return new ANQPNetworkKey(null, 0, hessid, anqpDomainId);
+ }
+ return new ANQPNetworkKey(ssid, 0, 0, anqpDomainId);
+ }
+
+ @Override
+ public int hashCode() {
+ if (mHESSID != 0) {
+ return (int) (((mHESSID >>> 32) * 31 + mHESSID) * 31 + mAnqpDomainID);
+ } else if (mBSSID != 0) {
+ return (int) ((mSSID.hashCode() * 31 + (mBSSID >>> 32)) * 31 + mBSSID);
+ } else {
+ return mSSID.hashCode() * 31 + mAnqpDomainID;
+ }
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ }
+ if (!(thatObject instanceof ANQPNetworkKey)) {
+ return false;
+ }
+ ANQPNetworkKey that = (ANQPNetworkKey) thatObject;
+ return TextUtils.equals(that.mSSID, mSSID)
+ && that.mBSSID == mBSSID
+ && that.mHESSID == mHESSID
+ && that.mAnqpDomainID == mAnqpDomainID;
+ }
+
+ @Override
+ public String toString() {
+ if (mHESSID != 0L) {
+ return Utils.macToString(mHESSID) + ":" + mAnqpDomainID;
+ } else if (mBSSID != 0L) {
+ return Utils.macToString(mBSSID)
+ + ":<" + Utils.toUnicodeEscapedString(mSSID) + ">";
+ } else {
+ return "<" + Utils.toUnicodeEscapedString(mSSID) + ">:" + mAnqpDomainID;
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPRequestManager.java b/service/java/com/android/server/wifi/hotspot2/ANQPRequestManager.java
new file mode 100644
index 0000000..a050b16
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/ANQPRequestManager.java
@@ -0,0 +1,211 @@
+/*
+ * 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.hotspot2;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Class for managing sending of ANQP requests. This manager will ignore ANQP requests for a
+ * period of time (hold off time) to a specified AP if the previous request to that AP goes
+ * unanswered or failed. The hold off time will increase exponentially until the max is reached.
+ */
+public class ANQPRequestManager {
+ private static final String TAG = "ANQPRequestManager";
+
+ private final PasspointEventHandler mPasspointHandler;
+ private final Clock mClock;
+
+ /**
+ * List of pending ANQP request associated with an AP (BSSID).
+ */
+ private final Map<Long, ANQPNetworkKey> mPendingQueries;
+
+ /**
+ * List of hold off time information associated with APs specified by their BSSID.
+ * Used to determine when an ANQP request can be send to the corresponding AP after the
+ * previous request goes unanswered or failed.
+ */
+ private final Map<Long, HoldOffInfo> mHoldOffInfo;
+
+ /**
+ * Minimum number of milliseconds to wait for before attempting ANQP queries to the same AP
+ * after previous request goes unanswered or failed.
+ */
+ @VisibleForTesting
+ public static final int BASE_HOLDOFF_TIME_MILLISECONDS = 10000;
+
+ /**
+ * Max value for the hold off counter for unanswered/failed queries. This limits the maximum
+ * hold off time to:
+ * BASE_HOLDOFF_TIME_MILLISECONDS * 2^MAX_HOLDOFF_COUNT
+ * which is 640 seconds.
+ */
+ @VisibleForTesting
+ public static final int MAX_HOLDOFF_COUNT = 6;
+
+ private static final List<Constants.ANQPElementType> R1_ANQP_BASE_SET = Arrays.asList(
+ Constants.ANQPElementType.ANQPVenueName,
+ Constants.ANQPElementType.ANQPIPAddrAvailability,
+ Constants.ANQPElementType.ANQPNAIRealm,
+ Constants.ANQPElementType.ANQP3GPPNetwork,
+ Constants.ANQPElementType.ANQPDomName);
+
+ private static final List<Constants.ANQPElementType> R2_ANQP_BASE_SET = Arrays.asList(
+ Constants.ANQPElementType.HSFriendlyName,
+ Constants.ANQPElementType.HSWANMetrics,
+ Constants.ANQPElementType.HSConnCapability,
+ Constants.ANQPElementType.HSOSUProviders);
+
+ /**
+ * Class to keep track of AP status for ANQP requests.
+ */
+ private class HoldOffInfo {
+ /**
+ * Current hold off count. Will max out at {@link #MAX_HOLDOFF_COUNT}.
+ */
+ public int holdOffCount;
+ /**
+ * The time stamp in milliseconds when we're allow to send ANQP request to the
+ * corresponding AP.
+ */
+ public long holdOffExpirationTime;
+ }
+
+ public ANQPRequestManager(PasspointEventHandler handler, Clock clock) {
+ mPasspointHandler = handler;
+ mClock = clock;
+ mPendingQueries = new HashMap<>();
+ mHoldOffInfo = new HashMap<>();
+ }
+
+ /**
+ * Request ANQP elements from the specified AP. This will request the basic Release 1 ANQP
+ * elements {@link #R1_ANQP_BASE_SET}. Additional elements will be requested based on the
+ * information provided in the Information Element (Roaming Consortium OI count and the
+ * supported Hotspot 2.0 release version).
+ *
+ * @param bssid The BSSID of the AP
+ * @param anqpNetworkKey The unique network key associated with this request
+ * @param rcOIs Flag indicating the inclusion of roaming consortium OIs. When set to true,
+ * Roaming Consortium ANQP element will be requested
+ * @param hsReleaseR2 Flag indicating the support of Hotspot 2.0 Release 2. When set to true,
+ * the Release 2 ANQP elements {@link #R2_ANQP_BASE_SET} will be requested
+ * @return true if a request was sent successfully
+ */
+ public boolean requestANQPElements(long bssid, ANQPNetworkKey anqpNetworkKey, boolean rcOIs,
+ boolean hsReleaseR2) {
+ // Check if we are allow to send the request now.
+ if (!canSendRequestNow(bssid)) {
+ return false;
+ }
+
+ // No need to hold off future requests for send failures.
+ if (!mPasspointHandler.requestANQP(bssid, getRequestElementIDs(rcOIs, hsReleaseR2))) {
+ return false;
+ }
+
+ // Update hold off info on when we are allowed to send the next ANQP request to
+ // the given AP.
+ updateHoldOffInfo(bssid);
+
+ mPendingQueries.put(bssid, anqpNetworkKey);
+ return true;
+ }
+
+ /**
+ * Notification of the completion of an ANQP request.
+ *
+ * @param bssid The BSSID of the AP
+ * @param success Flag indicating the result of the query
+ * @return {@link ANQPNetworkKey} associated with the completed request
+ */
+ public ANQPNetworkKey onRequestCompleted(long bssid, boolean success) {
+ if (success) {
+ // Query succeeded. No need to hold off request to the given AP.
+ mHoldOffInfo.remove(bssid);
+ }
+ return mPendingQueries.remove(bssid);
+ }
+
+ /**
+ * Check if we are allowed to send ANQP request to the specified AP now.
+ *
+ * @param bssid The BSSID of an AP
+ * @return true if we are allowed to send the request now
+ */
+ private boolean canSendRequestNow(long bssid) {
+ long currentTime = mClock.getElapsedSinceBootMillis();
+ HoldOffInfo info = mHoldOffInfo.get(bssid);
+ if (info != null && info.holdOffExpirationTime > currentTime) {
+ Log.d(TAG, "Not allowed to send ANQP request to " + bssid + " for another "
+ + (info.holdOffExpirationTime - currentTime) / 1000 + " seconds");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Update the ANQP request hold off info associated with the given AP.
+ *
+ * @param bssid The BSSID of an AP
+ */
+ private void updateHoldOffInfo(long bssid) {
+ HoldOffInfo info = mHoldOffInfo.get(bssid);
+ if (info == null) {
+ info = new HoldOffInfo();
+ mHoldOffInfo.put(bssid, info);
+ }
+ info.holdOffExpirationTime = mClock.getElapsedSinceBootMillis()
+ + BASE_HOLDOFF_TIME_MILLISECONDS * (1 << info.holdOffCount);
+ if (info.holdOffCount < MAX_HOLDOFF_COUNT) {
+ info.holdOffCount++;
+ }
+ }
+
+ /**
+ * Get the list of ANQP element IDs to request based on the Hotspot 2.0 release number
+ * and the ANQP OI count indicated in the Information Element.
+ *
+ * @param rcOIs Flag indicating the inclusion of roaming consortium OIs
+ * @param hsReleaseR2 Flag indicating support of Hotspot 2.0 Release 2
+ * @return List of ANQP Element ID
+ */
+ private static List<Constants.ANQPElementType> getRequestElementIDs(boolean rcOIs,
+ boolean hsReleaseR2) {
+ List<Constants.ANQPElementType> requestList = new ArrayList<>();
+ requestList.addAll(R1_ANQP_BASE_SET);
+ if (rcOIs) {
+ requestList.add(Constants.ANQPElementType.ANQPRoamingConsortium);
+ }
+
+ if (hsReleaseR2) {
+ requestList.addAll(R2_ANQP_BASE_SET);
+ }
+ return requestList;
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/AnqpCache.java b/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
index a6cd42e..215f6a3 100644
--- a/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
+++ b/service/java/com/android/server/wifi/hotspot2/AnqpCache.java
@@ -1,10 +1,25 @@
+/*
+ * 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.hotspot2;
-import android.util.Log;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wifi.Clock;
-import com.android.server.wifi.anqp.ANQPElement;
-import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -12,194 +27,77 @@
import java.util.List;
import java.util.Map;
+/**
+ * Cache for storing ANQP data. This is simply a data cache, all the logic related to
+ * ANQP data query will be handled elsewhere (e.g. the consumer of the cache).
+ */
public class AnqpCache {
- private static final boolean DBG = false;
+ @VisibleForTesting
+ public static final long CACHE_SWEEP_INTERVAL_MILLISECONDS = 60000L;
- private static final long CACHE_RECHECK = 60000L;
- private static final boolean STANDARD_ESS = true; // Regular AP keying; see CacheKey below.
private long mLastSweep;
private Clock mClock;
- private final HashMap<CacheKey, ANQPData> mANQPCache;
+ private final Map<ANQPNetworkKey, ANQPData> mANQPCache;
public AnqpCache(Clock clock) {
mClock = clock;
mANQPCache = new HashMap<>();
- mLastSweep = mClock.currentTimeMillis();
+ mLastSweep = mClock.getElapsedSinceBootMillis();
}
- private static class CacheKey {
- private final String mSSID;
- private final long mBSSID;
- private final long mHESSID;
-
- private CacheKey(String ssid, long bssid, long hessid) {
- mSSID = ssid;
- mBSSID = bssid;
- mHESSID = hessid;
- }
-
- /**
- * Build an ANQP cache key suitable for the granularity of the key space as follows:
- *
- * HESSID domainID standardESS Key content Rationale
- * -------- ----------- --------------- ----------- --------------------
- * n/a zero n/a SSID/BSSID Domain ID indicates unique AP info
- * not set set false SSID/BSSID Strict per AP keying override
- * not set set true SSID Standard definition of an ESS
- * set set n/a HESSID The ESS is defined by the HESSID
- *
- * @param network The network to build the key for.
- * @param standardESS If this parameter is set the "standard" paradigm for an ESS is used
- * for the cache, i.e. all APs with identical SSID is considered an ESS,
- * otherwise caching is performed per AP.
- * @return A CacheKey.
- */
- private static CacheKey buildKey(NetworkDetail network, boolean standardESS) {
- String ssid;
- long bssid;
- long hessid;
- if (network.getAnqpDomainID() == 0L || (network.getHESSID() == 0L && !standardESS)) {
- ssid = network.getSSID();
- bssid = network.getBSSID();
- hessid = 0L;
- }
- else if (network.getHESSID() != 0L && network.getAnqpDomainID() > 0) {
- ssid = null;
- bssid = 0L;
- hessid = network.getHESSID();
- }
- else {
- ssid = network.getSSID();
- bssid = 0L;
- hessid = 0L;
- }
-
- return new CacheKey(ssid, bssid, hessid);
- }
-
- @Override
- public int hashCode() {
- if (mHESSID != 0) {
- return (int)((mHESSID >>> 32) * 31 + mHESSID);
- }
- else if (mBSSID != 0) {
- return (int)((mSSID.hashCode() * 31 + (mBSSID >>> 32)) * 31 + mBSSID);
- }
- else {
- return mSSID.hashCode();
- }
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (thatObject == this) {
- return true;
- }
- else if (thatObject == null || thatObject.getClass() != CacheKey.class) {
- return false;
- }
- CacheKey that = (CacheKey) thatObject;
- return Utils.compare(that.mSSID, mSSID) == 0 &&
- that.mBSSID == mBSSID &&
- that.mHESSID == mHESSID;
- }
-
- @Override
- public String toString() {
- if (mHESSID != 0L) {
- return "HESSID:" + NetworkDetail.toMACString(mHESSID);
- }
- else if (mBSSID != 0L) {
- return NetworkDetail.toMACString(mBSSID) +
- ":<" + Utils.toUnicodeEscapedString(mSSID) + ">";
- }
- else {
- return '<' + Utils.toUnicodeEscapedString(mSSID) + '>';
- }
- }
+ /**
+ * Add an ANQP entry associated with the given key.
+ *
+ * @param key The key that's associated with the entry
+ * @param anqpElements The ANQP elements from the AP
+ */
+ public void addEntry(ANQPNetworkKey key,
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+ ANQPData data = new ANQPData(mClock, anqpElements);
+ mANQPCache.put(key, data);
}
- public List<Constants.ANQPElementType> initiate(NetworkDetail network,
- List<Constants.ANQPElementType> querySet) {
- CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
-
- synchronized (mANQPCache) {
- ANQPData data = mANQPCache.get(key);
- if (data == null || data.expired()) {
- mANQPCache.put(key, new ANQPData(mClock, network, data));
- return querySet;
- }
- else {
- List<Constants.ANQPElementType> newList = data.disjoint(querySet);
- Log.d(Utils.hs2LogTag(getClass()),
- String.format("New ANQP elements for BSSID %012x: %s",
- network.getBSSID(), newList));
- return newList;
- }
- }
+ /**
+ * Get the ANQP data associated with the given AP.
+ *
+ * @param key The key that's associated with the entry
+ * @return {@link ANQPData}
+ */
+ public ANQPData getEntry(ANQPNetworkKey key) {
+ return mANQPCache.get(key);
}
- public void update(NetworkDetail network,
- Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
-
- CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
-
- // Networks with a 0 ANQP Domain ID are still cached, but with a very short expiry, just
- // long enough to prevent excessive re-querying.
- synchronized (mANQPCache) {
- ANQPData data = mANQPCache.get(key);
- if (data != null && data.hasData()) {
- data.merge(anqpElements);
- }
- else {
- data = new ANQPData(mClock, network, anqpElements);
- mANQPCache.put(key, data);
- }
- }
- }
-
- public ANQPData getEntry(NetworkDetail network) {
- ANQPData data;
-
- CacheKey key = CacheKey.buildKey(network, STANDARD_ESS);
- synchronized (mANQPCache) {
- data = mANQPCache.get(key);
+ /**
+ * Go through the cache to remove any expired entries.
+ */
+ public void sweep() {
+ long now = mClock.getElapsedSinceBootMillis();
+ // Check if it is time to perform the sweep.
+ if (now < mLastSweep + CACHE_SWEEP_INTERVAL_MILLISECONDS) {
+ return;
}
- return data != null && data.isValid(network) ? data : null;
- }
-
- public void clear(boolean all, boolean debug) {
- if (DBG) Log.d(Utils.hs2LogTag(getClass()), "Clearing ANQP cache: all: " + all);
- long now = mClock.currentTimeMillis();
- synchronized (mANQPCache) {
- if (all) {
- mANQPCache.clear();
- mLastSweep = now;
- }
- else if (now > mLastSweep + CACHE_RECHECK) {
- List<CacheKey> retirees = new ArrayList<>();
- for (Map.Entry<CacheKey, ANQPData> entry : mANQPCache.entrySet()) {
- if (entry.getValue().expired(now)) {
- retirees.add(entry.getKey());
- }
- }
- for (CacheKey key : retirees) {
- mANQPCache.remove(key);
- if (debug) {
- Log.d(Utils.hs2LogTag(getClass()), "Retired " + key);
- }
- }
- mLastSweep = now;
+ // Get all expired keys.
+ List<ANQPNetworkKey> expiredKeys = new ArrayList<>();
+ for (Map.Entry<ANQPNetworkKey, ANQPData> entry : mANQPCache.entrySet()) {
+ if (entry.getValue().expired(now)) {
+ expiredKeys.add(entry.getKey());
}
}
+
+ // Remove all expired entries.
+ for (ANQPNetworkKey key : expiredKeys) {
+ mANQPCache.remove(key);
+ }
+ mLastSweep = now;
}
public void dump(PrintWriter out) {
- out.println("Last sweep " + Utils.toHMS(mClock.currentTimeMillis() - mLastSweep) + " ago.");
- for (ANQPData anqpData : mANQPCache.values()) {
- out.println(anqpData.toString(false));
+ out.println("Last sweep " + Utils.toHMS(mClock.getElapsedSinceBootMillis() - mLastSweep)
+ + " ago.");
+ for (Map.Entry<ANQPNetworkKey, ANQPData> entry : mANQPCache.entrySet()) {
+ out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
diff --git a/service/java/com/android/server/wifi/hotspot2/AnqpEvent.java b/service/java/com/android/server/wifi/hotspot2/AnqpEvent.java
new file mode 100644
index 0000000..0fe8a3a
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/AnqpEvent.java
@@ -0,0 +1,75 @@
+/*
+ * 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.hotspot2;
+
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class carries the response of an ANQP request.
+ */
+public class AnqpEvent {
+ private static final String TAG = "AnqpEvent";
+ private static final Map<String, Constants.ANQPElementType> sWpsNames = new HashMap<>();
+
+ static {
+ sWpsNames.put("anqp_venue_name", Constants.ANQPElementType.ANQPVenueName);
+ sWpsNames.put("anqp_roaming_consortium", Constants.ANQPElementType.ANQPRoamingConsortium);
+ sWpsNames.put("anqp_ip_addr_type_availability",
+ Constants.ANQPElementType.ANQPIPAddrAvailability);
+ sWpsNames.put("anqp_nai_realm", Constants.ANQPElementType.ANQPNAIRealm);
+ sWpsNames.put("anqp_3gpp", Constants.ANQPElementType.ANQP3GPPNetwork);
+ sWpsNames.put("anqp_domain_name", Constants.ANQPElementType.ANQPDomName);
+ sWpsNames.put("hs20_operator_friendly_name", Constants.ANQPElementType.HSFriendlyName);
+ sWpsNames.put("hs20_wan_metrics", Constants.ANQPElementType.HSWANMetrics);
+ sWpsNames.put("hs20_connection_capability", Constants.ANQPElementType.HSConnCapability);
+ sWpsNames.put("hs20_osu_providers_list", Constants.ANQPElementType.HSOSUProviders);
+ }
+
+ /**
+ * Bssid of the access point.
+ */
+ private final long mBssid;
+
+ /**
+ * Map of ANQP element type to the data retrieved from the access point.
+ */
+ private final Map<Constants.ANQPElementType, ANQPElement> mElements;
+
+ public AnqpEvent(long bssid, Map<Constants.ANQPElementType, ANQPElement> elements) {
+ mBssid = bssid;
+ mElements = elements;
+ }
+
+ /**
+ * Get the bssid of the access point from which this ANQP result was created.
+ */
+ public long getBssid() {
+ return mBssid;
+ }
+
+ /**
+ * Get the map of ANQP elements retrieved from the access point.
+ */
+ public Map<Constants.ANQPElementType, ANQPElement> getElements() {
+ return mElements;
+ }
+
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/AuthMatch.java b/service/java/com/android/server/wifi/hotspot2/AuthMatch.java
index cd988b5..3abf35f 100644
--- a/service/java/com/android/server/wifi/hotspot2/AuthMatch.java
+++ b/service/java/com/android/server/wifi/hotspot2/AuthMatch.java
@@ -1,3 +1,18 @@
+/*
+ * 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.hotspot2;
/**
@@ -9,13 +24,13 @@
* must be maintained accordingly.
*/
public abstract class AuthMatch {
- public static final int None = -1;
- public static final int Indeterminate = 0;
- public static final int Realm = 0x04;
- public static final int Method = 0x02;
- public static final int Param = 0x01;
- public static final int MethodParam = Method | Param;
- public static final int Exact = Realm | Method | Param;
+ public static final int NONE = -1;
+ public static final int INDETERMINATE = 0;
+ public static final int REALM = 0x04;
+ public static final int METHOD = 0x02;
+ public static final int PARAM = 0x01;
+ public static final int METHOD_PARAM = METHOD | PARAM;
+ public static final int EXACT = REALM | METHOD | PARAM;
public static String toString(int match) {
if (match < 0) {
@@ -26,13 +41,13 @@
}
StringBuilder sb = new StringBuilder();
- if ((match & Realm) != 0) {
+ if ((match & REALM) != 0) {
sb.append("Realm");
}
- if ((match & Method) != 0) {
+ if ((match & METHOD) != 0) {
sb.append("Method");
}
- if ((match & Param) != 0) {
+ if ((match & PARAM) != 0) {
sb.append("Param");
}
return sb.toString();
diff --git a/service/java/com/android/server/wifi/hotspot2/CertificateVerifier.java b/service/java/com/android/server/wifi/hotspot2/CertificateVerifier.java
new file mode 100644
index 0000000..004a32f
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/CertificateVerifier.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXParameters;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+
+/**
+ * Utility class used for verifying certificates against the pre-loaded public CAs in the
+ * system key store. This class is created to allow the certificate verification to be mocked in
+ * unit tests.
+ */
+public class CertificateVerifier {
+
+ /**
+ * Verify that the given certificate is trusted by one of the pre-loaded public CAs in the
+ * system key store.
+ *
+ * @param caCert The CA Certificate to verify
+ * @throws GeneralSecurityException
+ * @throws IOException
+ */
+ public void verifyCaCert(X509Certificate caCert)
+ throws GeneralSecurityException, IOException {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ CertPathValidator validator =
+ CertPathValidator.getInstance(CertPathValidator.getDefaultType());
+ CertPath path = factory.generateCertPath(
+ Arrays.asList(caCert));
+ KeyStore ks = KeyStore.getInstance("AndroidCAStore");
+ ks.load(null, null);
+ PKIXParameters params = new PKIXParameters(ks);
+ params.setRevocationEnabled(false);
+ validator.validate(path, params);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/DomainMatcher.java b/service/java/com/android/server/wifi/hotspot2/DomainMatcher.java
new file mode 100644
index 0000000..ce60c55
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/DomainMatcher.java
@@ -0,0 +1,215 @@
+/*
+ * 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.hotspot2;
+
+import android.text.TextUtils;
+
+import com.android.server.wifi.hotspot2.Utils;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class for matching domain names.
+ */
+public class DomainMatcher {
+ public static final int MATCH_NONE = 0;
+ public static final int MATCH_PRIMARY = 1;
+ public static final int MATCH_SECONDARY = 2;
+
+ /**
+ * The root of the Label tree.
+ */
+ private final Label mRoot;
+
+ /**
+ * Label tree representation for the domain name. Labels are delimited by "." in the domain
+ * name.
+ *
+ * For example, the tree representation of "android.google.com" as a primary domain:
+ * [com, None] -> [google, None] -> [android, Primary]
+ *
+ */
+ private static class Label {
+ private final Map<String, Label> mSubDomains;
+ private int mMatch;
+
+ Label(int match) {
+ mMatch = match;
+ mSubDomains = new HashMap<String, Label>();
+ }
+
+ /**
+ * Add sub-domains to this label.
+ *
+ * @param labels The iterator of domain label strings
+ * @param match The match status of the domain
+ */
+ public void addDomain(Iterator<String> labels, int match) {
+ String labelName = labels.next();
+ // Create the Label object if it doesn't exist yet.
+ Label subLabel = mSubDomains.get(labelName);
+ if (subLabel == null) {
+ subLabel = new Label(MATCH_NONE);
+ mSubDomains.put(labelName, subLabel);
+ }
+
+ if (labels.hasNext()) {
+ // Adding sub-domain.
+ subLabel.addDomain(labels, match);
+ } else {
+ // End of the domain, update the match status.
+ subLabel.mMatch = match;
+ }
+ }
+
+ /**
+ * Return the Label for the give label string.
+ * @param labelString The label string to look for
+ * @return {@link Label}
+ */
+ public Label getSubLabel(String labelString) {
+ return mSubDomains.get(labelString);
+ }
+
+ /**
+ * Return the match status
+ *
+ * @return The match status
+ */
+ public int getMatch() {
+ return mMatch;
+ }
+
+ private void toString(StringBuilder sb) {
+ if (mSubDomains != null) {
+ sb.append(".{");
+ for (Map.Entry<String, Label> entry : mSubDomains.entrySet()) {
+ sb.append(entry.getKey());
+ entry.getValue().toString(sb);
+ }
+ sb.append('}');
+ } else {
+ sb.append('=').append(mMatch);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ toString(sb);
+ return sb.toString();
+ }
+ }
+
+ public DomainMatcher(String primaryDomain, List<String> secondaryDomains) {
+ // Create the root label.
+ mRoot = new Label(MATCH_NONE);
+
+ // Add secondary domains.
+ if (secondaryDomains != null) {
+ for (String domain : secondaryDomains) {
+ if (!TextUtils.isEmpty(domain)) {
+ List<String> secondaryLabel = Utils.splitDomain(domain);
+ mRoot.addDomain(secondaryLabel.iterator(), MATCH_SECONDARY);
+ }
+ }
+ }
+
+ // Add primary domain, primary overwrites secondary.
+ if (!TextUtils.isEmpty(primaryDomain)) {
+ List<String> primaryLabel = Utils.splitDomain(primaryDomain);
+ mRoot.addDomain(primaryLabel.iterator(), MATCH_PRIMARY);
+ }
+ }
+
+ /**
+ * Check if domain is either the same or a sub-domain of any of the domains in the
+ * domain tree in this matcher, i.e. all or a sub-set of the labels in domain matches
+ * a path in the tree.
+ *
+ * This will have precedence for matching primary domain over secondary domain if both
+ * are found.
+ *
+ * For example, with primary domain set to "test.google.com" and secondary domain set to
+ * "google.com":
+ * "test2.test.google.com" -> Match.Primary
+ * "test1.google.com" -> Match.Secondary
+ *
+ * @param domainName Domain name to be checked.
+ * @return The match status
+ */
+ public int isSubDomain(String domainName) {
+ if (TextUtils.isEmpty(domainName)) {
+ return MATCH_NONE;
+ }
+ List<String> domainLabels = Utils.splitDomain(domainName);
+
+ Label label = mRoot;
+ int match = MATCH_NONE;
+ for (String labelString : domainLabels) {
+ label = label.getSubLabel(labelString);
+ if (label == null) {
+ break;
+ } else if (label.getMatch() != MATCH_NONE) {
+ match = label.getMatch();
+ if (match == MATCH_PRIMARY) {
+ break;
+ }
+ }
+ }
+ return match;
+ }
+
+ /**
+ * Check if domain2 is a sub-domain of domain1.
+ *
+ * @param domain1 The string of the first domain
+ * @param domain2 The string of the second domain
+ * @return true if the second domain is the sub-domain of the first
+ */
+ public static boolean arg2SubdomainOfArg1(String domain1, String domain2) {
+ if (TextUtils.isEmpty(domain1) || TextUtils.isEmpty(domain2)) {
+ return false;
+ }
+
+ List<String> labels1 = Utils.splitDomain(domain1);
+ List<String> labels2 = Utils.splitDomain(domain2);
+
+ // domain2 must be the same or longer than domain1 in order to be a sub-domain.
+ if (labels2.size() < labels1.size()) {
+ return false;
+ }
+
+ Iterator<String> l1 = labels1.iterator();
+ Iterator<String> l2 = labels2.iterator();
+
+ while(l1.hasNext()) {
+ if (!TextUtils.equals(l1.next(), l2.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Domain matcher " + mRoot;
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/IconEvent.java b/service/java/com/android/server/wifi/hotspot2/IconEvent.java
index 69a3b27..406c03c 100644
--- a/service/java/com/android/server/wifi/hotspot2/IconEvent.java
+++ b/service/java/com/android/server/wifi/hotspot2/IconEvent.java
@@ -4,11 +4,13 @@
private final long mBSSID;
private final String mFileName;
private final int mSize;
+ private final byte[] mData;
- public IconEvent(long bssid, String fileName, int size) {
+ public IconEvent(long bssid, String fileName, int size, byte[] data) {
mBSSID = bssid;
mFileName = fileName;
mSize = size;
+ mData = data;
}
public long getBSSID() {
@@ -23,6 +25,10 @@
return mSize;
}
+ public byte[] getData() {
+ return mData;
+ }
+
@Override
public String toString() {
return "IconEvent: " +
diff --git a/service/java/com/android/server/wifi/hotspot2/LegacyPasspointConfig.java b/service/java/com/android/server/wifi/hotspot2/LegacyPasspointConfig.java
new file mode 100644
index 0000000..5f455f5
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/LegacyPasspointConfig.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.text.TextUtils;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Class representing the relevant configurations in the legacy Passpoint (N and older)
+ * configuration file (/data/misc/wifi/PerProviderSubscription.conf). Most of the configurations
+ * (e.g. user credential) are saved elsewhere, the relevant configurations in this file are:
+ * - FQDN
+ * - Friendly Name
+ * - Roaming Consortium
+ * - Realm
+ * - IMSI (for SIM credential)
+ */
+public class LegacyPasspointConfig {
+ public String mFqdn;
+ public String mFriendlyName;
+ public long[] mRoamingConsortiumOis;
+ public String mRealm;
+ public String mImsi;
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof LegacyPasspointConfig)) {
+ return false;
+ }
+ LegacyPasspointConfig that = (LegacyPasspointConfig) thatObject;
+ return TextUtils.equals(mFqdn, that.mFqdn)
+ && TextUtils.equals(mFriendlyName, that.mFriendlyName)
+ && Arrays.equals(mRoamingConsortiumOis, that.mRoamingConsortiumOis)
+ && TextUtils.equals(mRealm, that.mRealm)
+ && TextUtils.equals(mImsi, that.mImsi);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFqdn, mFriendlyName, mRoamingConsortiumOis, mRealm, mImsi);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/LegacyPasspointConfigParser.java b/service/java/com/android/server/wifi/hotspot2/LegacyPasspointConfigParser.java
new file mode 100644
index 0000000..31795f1
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/LegacyPasspointConfigParser.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class for parsing legacy (N and older) Passpoint configuration file content
+ * (/data/misc/wifi/PerProviderSubscription.conf). In N and older, only Release 1 is supported.
+ *
+ * This class only retrieve the relevant Release 1 configuration fields that are not backed
+ * elsewhere. Below are relevant fields:
+ * - FQDN (used for linking with configuration data stored elsewhere)
+ * - Friendly Name
+ * - Roaming Consortium
+ * - Realm
+ * - IMSI (for SIM credential)
+ *
+ * Below is an example content of a Passpoint configuration file:
+ *
+ * tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)
+ * 8:MgmtTree+
+ * 17:PerProviderSubscription+
+ * 4:r1i1+
+ * 6:HomeSP+
+ * c:FriendlyName=d:Test Provider
+ * 4:FQDN=8:test.net
+ * 13:RoamingConsortiumOI=9:1234,5678
+ * .
+ * a:Credential+
+ * 10:UsernamePassword+
+ * 8:Username=4:user
+ * 8:Password=4:pass
+ *
+ * 9:EAPMethod+
+ * 7:EAPType=2:21
+ * b:InnerMethod=3:PAP
+ * .
+ * .
+ * 5:Realm=a:boingo.com
+ * .
+ * .
+ * .
+ * .
+ *
+ * Each string is prefixed with a "|StringBytesInHex|:".
+ * '+' indicates start of a new internal node.
+ * '.' indicates end of the current internal node.
+ * '=' indicates "value" of a leaf node.
+ *
+ */
+public class LegacyPasspointConfigParser {
+ private static final String TAG = "LegacyPasspointConfigParser";
+
+ private static final String TAG_MANAGEMENT_TREE = "MgmtTree";
+ private static final String TAG_PER_PROVIDER_SUBSCRIPTION = "PerProviderSubscription";
+ private static final String TAG_HOMESP = "HomeSP";
+ private static final String TAG_FQDN = "FQDN";
+ private static final String TAG_FRIENDLY_NAME = "FriendlyName";
+ private static final String TAG_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI";
+ private static final String TAG_CREDENTIAL = "Credential";
+ private static final String TAG_REALM = "Realm";
+ private static final String TAG_SIM = "SIM";
+ private static final String TAG_IMSI = "IMSI";
+
+ private static final String LONG_ARRAY_SEPARATOR = ",";
+ private static final String END_OF_INTERNAL_NODE_INDICATOR = ".";
+ private static final char START_OF_INTERNAL_NODE_INDICATOR = '+';
+ private static final char STRING_PREFIX_INDICATOR = ':';
+ private static final char STRING_VALUE_INDICATOR = '=';
+
+ /**
+ * An abstraction for a node within a tree. A node can be an internal node (contained
+ * children nodes) or a leaf node (contained a String value).
+ */
+ private abstract static class Node {
+ private final String mName;
+ Node(String name) {
+ mName = name;
+ }
+
+ /**
+ * @return the name of the node
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Applies for internal node only.
+ *
+ * @return the list of children nodes.
+ */
+ public abstract List<Node> getChildren();
+
+ /**
+ * Applies for leaf node only.
+ *
+ * @return the string value of the node
+ */
+ public abstract String getValue();
+ }
+
+ /**
+ * Class representing an internal node of a tree. It contained a list of child nodes.
+ */
+ private static class InternalNode extends Node {
+ private final List<Node> mChildren;
+ InternalNode(String name, List<Node> children) {
+ super(name);
+ mChildren = children;
+ }
+
+ @Override
+ public List<Node> getChildren() {
+ return mChildren;
+ }
+
+ @Override
+ public String getValue() {
+ return null;
+ }
+ }
+
+ /**
+ * Class representing a leaf node of a tree. It contained a String type value.
+ */
+ private static class LeafNode extends Node {
+ private final String mValue;
+ LeafNode(String name, String value) {
+ super(name);
+ mValue = value;
+ }
+
+ @Override
+ public List<Node> getChildren() {
+ return null;
+ }
+
+ @Override
+ public String getValue() {
+ return mValue;
+ }
+ }
+
+ public LegacyPasspointConfigParser() {}
+
+ /**
+ * Parse the legacy Passpoint configuration file content, only retrieve the relevant
+ * configurations that are not saved elsewhere.
+ *
+ * For both N and M, only Release 1 is supported. Most of the configurations are saved
+ * elsewhere as part of the {@link android.net.wifi.WifiConfiguration} data.
+ * The configurations needed from the legacy Passpoint configuration file are:
+ *
+ * - FQDN - needed to be able to link to the associated {@link WifiConfiguration} data
+ * - Friendly Name
+ * - Roaming Consortium OIs
+ * - Realm
+ * - IMSI (for SIM credential)
+ *
+ * Make this function non-static so that it can be mocked during unit test.
+ *
+ * @param fileName The file name of the configuration file
+ * @return Map of FQDN to {@link LegacyPasspointConfig}
+ * @throws IOException
+ */
+ public Map<String, LegacyPasspointConfig> parseConfig(String fileName)
+ throws IOException {
+ Map<String, LegacyPasspointConfig> configs = new HashMap<>();
+ BufferedReader in = new BufferedReader(new FileReader(fileName));
+ in.readLine(); // Ignore the first line which contained the header.
+
+ // Convert the configuration data to a management tree represented by a root {@link Node}.
+ Node root = buildNode(in);
+
+ if (root == null || root.getChildren() == null) {
+ Log.d(TAG, "Empty configuration data");
+ return configs;
+ }
+
+ // Verify root node name.
+ if (!TextUtils.equals(TAG_MANAGEMENT_TREE, root.getName())) {
+ throw new IOException("Unexpected root node: " + root.getName());
+ }
+
+ // Process and retrieve the configuration from each PPS (PerProviderSubscription) node.
+ List<Node> ppsNodes = root.getChildren();
+ for (Node ppsNode : ppsNodes) {
+ LegacyPasspointConfig config = processPpsNode(ppsNode);
+ configs.put(config.mFqdn, config);
+ }
+ return configs;
+ }
+
+ /**
+ * Build a {@link Node} from the current line in the buffer. A node can be an internal
+ * node (ends with '+') or a leaf node.
+ *
+ * @param in Input buffer to read data from
+ * @return {@link Node} representing the current line
+ * @throws IOException
+ */
+ private static Node buildNode(BufferedReader in) throws IOException {
+ // Read until non-empty line.
+ String currentLine = null;
+ while ((currentLine = in.readLine()) != null) {
+ if (!currentLine.isEmpty()) {
+ break;
+ }
+ }
+
+ // Return null if EOF is reached.
+ if (currentLine == null) {
+ return null;
+ }
+
+ // Remove the leading and the trailing whitespaces.
+ currentLine = currentLine.trim();
+
+ // Check for the internal node terminator.
+ if (TextUtils.equals(END_OF_INTERNAL_NODE_INDICATOR, currentLine)) {
+ return null;
+ }
+
+ // Parse the name-value of the current line. The value will be null if the current line
+ // is not a leaf node (e.g. line ends with a '+').
+ // Each line is encoded in UTF-8.
+ Pair<String, String> nameValuePair =
+ parseLine(currentLine.getBytes(StandardCharsets.UTF_8));
+ if (nameValuePair.second != null) {
+ return new LeafNode(nameValuePair.first, nameValuePair.second);
+ }
+
+ // Parse the children contained under this internal node.
+ List<Node> children = new ArrayList<>();
+ Node child = null;
+ while ((child = buildNode(in)) != null) {
+ children.add(child);
+ }
+ return new InternalNode(nameValuePair.first, children);
+ }
+
+ /**
+ * Process a PPS (PerProviderSubscription) node to retrieve Passpoint configuration data.
+ *
+ * @param ppsNode The PPS node to process
+ * @return {@link LegacyPasspointConfig}
+ * @throws IOException
+ */
+ private static LegacyPasspointConfig processPpsNode(Node ppsNode) throws IOException {
+ if (ppsNode.getChildren() == null || ppsNode.getChildren().size() != 1) {
+ throw new IOException("PerProviderSubscription node should contain "
+ + "one instance node");
+ }
+
+ if (!TextUtils.equals(TAG_PER_PROVIDER_SUBSCRIPTION, ppsNode.getName())) {
+ throw new IOException("Unexpected name for PPS node: " + ppsNode.getName());
+ }
+
+ // Retrieve the PPS instance node.
+ Node instanceNode = ppsNode.getChildren().get(0);
+ if (instanceNode.getChildren() == null) {
+ throw new IOException("PPS instance node doesn't contained any children");
+ }
+
+ // Process and retrieve the relevant configurations under the PPS instance node.
+ LegacyPasspointConfig config = new LegacyPasspointConfig();
+ for (Node node : instanceNode.getChildren()) {
+ switch (node.getName()) {
+ case TAG_HOMESP:
+ processHomeSPNode(node, config);
+ break;
+ case TAG_CREDENTIAL:
+ processCredentialNode(node, config);
+ break;
+ default:
+ Log.d(TAG, "Ignore uninterested field under PPS instance: " + node.getName());
+ break;
+ }
+ }
+ if (config.mFqdn == null) {
+ throw new IOException("PPS instance missing FQDN");
+ }
+ return config;
+ }
+
+ /**
+ * Process a HomeSP node to retrieve configuration data into the given |config|.
+ *
+ * @param homeSpNode The HomeSP node to process
+ * @param config The config object to fill in the data
+ * @throws IOException
+ */
+ private static void processHomeSPNode(Node homeSpNode, LegacyPasspointConfig config)
+ throws IOException {
+ if (homeSpNode.getChildren() == null) {
+ throw new IOException("HomeSP node should contain at least one child node");
+ }
+
+ for (Node node : homeSpNode.getChildren()) {
+ switch (node.getName()) {
+ case TAG_FQDN:
+ config.mFqdn = getValue(node);
+ break;
+ case TAG_FRIENDLY_NAME:
+ config.mFriendlyName = getValue(node);
+ break;
+ case TAG_ROAMING_CONSORTIUM_OI:
+ config.mRoamingConsortiumOis = parseLongArray(getValue(node));
+ break;
+ default:
+ Log.d(TAG, "Ignore uninterested field under HomeSP: " + node.getName());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Process a Credential node to retrieve configuration data into the given |config|.
+ *
+ * @param credentialNode The Credential node to process
+ * @param config The config object to fill in the data
+ * @throws IOException
+ */
+ private static void processCredentialNode(Node credentialNode,
+ LegacyPasspointConfig config)
+ throws IOException {
+ if (credentialNode.getChildren() == null) {
+ throw new IOException("Credential node should contain at least one child node");
+ }
+
+ for (Node node : credentialNode.getChildren()) {
+ switch (node.getName()) {
+ case TAG_REALM:
+ config.mRealm = getValue(node);
+ break;
+ case TAG_SIM:
+ processSimNode(node, config);
+ break;
+ default:
+ Log.d(TAG, "Ignore uninterested field under Credential: " + node.getName());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Process a SIM node to retrieve configuration data into the given |config|.
+ *
+ * @param simNode The SIM node to process
+ * @param config The config object to fill in the data
+ * @throws IOException
+ */
+ private static void processSimNode(Node simNode, LegacyPasspointConfig config)
+ throws IOException {
+ if (simNode.getChildren() == null) {
+ throw new IOException("SIM node should contain at least one child node");
+ }
+
+ for (Node node : simNode.getChildren()) {
+ switch (node.getName()) {
+ case TAG_IMSI:
+ config.mImsi = getValue(node);
+ break;
+ default:
+ Log.d(TAG, "Ignore uninterested field under SIM: " + node.getName());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parse the given line in the legacy Passpoint configuration file.
+ * A line can be in the following formats:
+ * 2:ab+ // internal node
+ * 2:ab=2:bc // leaf node
+ * . // end of internal node
+ *
+ * @param line The line to parse
+ * @return name-value pair, a value of null indicates internal node
+ * @throws IOException
+ */
+ private static Pair<String, String> parseLine(byte[] lineBytes) throws IOException {
+ Pair<String, Integer> nameIndexPair = parseString(lineBytes, 0);
+ int currentIndex = nameIndexPair.second;
+ try {
+ if (lineBytes[currentIndex] == START_OF_INTERNAL_NODE_INDICATOR) {
+ return Pair.create(nameIndexPair.first, null);
+ }
+
+ if (lineBytes[currentIndex] != STRING_VALUE_INDICATOR) {
+ throw new IOException("Invalid line - missing both node and value indicator: "
+ + new String(lineBytes, StandardCharsets.UTF_8));
+ }
+ } catch (IndexOutOfBoundsException e) {
+ throw new IOException("Invalid line - " + e.getMessage() + ": "
+ + new String(lineBytes, StandardCharsets.UTF_8));
+ }
+ Pair<String, Integer> valueIndexPair = parseString(lineBytes, currentIndex + 1);
+ return Pair.create(nameIndexPair.first, valueIndexPair.first);
+ }
+
+ /**
+ * Parse a string value in the given line from the given start index.
+ * A string value is in the following format:
+ * |HexByteLength|:|String|
+ *
+ * The length value indicates the number of UTF-8 bytes in hex for the given string.
+ *
+ * For example: 3:abc
+ *
+ * @param lineBytes The UTF-8 bytes of the line to parse
+ * @param startIndex The start index from the given line to parse from
+ * @return Pair of a string value and an index pointed to character after the string value
+ * @throws IOException
+ */
+ private static Pair<String, Integer> parseString(byte[] lineBytes, int startIndex)
+ throws IOException {
+ // Locate the index that separate length and the string value.
+ int prefixIndex = -1;
+ for (int i = startIndex; i < lineBytes.length; i++) {
+ if (lineBytes[i] == STRING_PREFIX_INDICATOR) {
+ prefixIndex = i;
+ break;
+ }
+ }
+ if (prefixIndex == -1) {
+ throw new IOException("Invalid line - missing string prefix: "
+ + new String(lineBytes, StandardCharsets.UTF_8));
+ }
+
+ try {
+ String lengthStr = new String(lineBytes, startIndex, prefixIndex - startIndex,
+ StandardCharsets.UTF_8);
+ int length = Integer.parseInt(lengthStr, 16);
+ int strStartIndex = prefixIndex + 1;
+ // The length might account for bytes for the whitespaces, since the whitespaces are
+ // already trimmed, ignore them.
+ if ((strStartIndex + length) > lineBytes.length) {
+ length = lineBytes.length - strStartIndex;
+ }
+ return Pair.create(
+ new String(lineBytes, strStartIndex, length, StandardCharsets.UTF_8),
+ strStartIndex + length);
+ } catch (NumberFormatException | IndexOutOfBoundsException e) {
+ throw new IOException("Invalid line - " + e.getMessage() + ": "
+ + new String(lineBytes, StandardCharsets.UTF_8));
+ }
+ }
+
+ /**
+ * Parse a long array from the given string.
+ *
+ * @param str The string to parse
+ * @return long[]
+ * @throws IOException
+ */
+ private static long[] parseLongArray(String str)
+ throws IOException {
+ String[] strArray = str.split(LONG_ARRAY_SEPARATOR);
+ long[] longArray = new long[strArray.length];
+ for (int i = 0; i < longArray.length; i++) {
+ try {
+ longArray[i] = Long.parseLong(strArray[i], 16);
+ } catch (NumberFormatException e) {
+ throw new IOException("Invalid long integer value: " + strArray[i]);
+ }
+ }
+ return longArray;
+ }
+
+ /**
+ * Get the String value of the given node. An IOException will be thrown if the given
+ * node doesn't contain a String value (internal node).
+ *
+ * @param node The node to get the value from
+ * @return String
+ * @throws IOException
+ */
+ private static String getValue(Node node) throws IOException {
+ if (node.getValue() == null) {
+ throw new IOException("Attempt to retreive value from non-leaf node: "
+ + node.getName());
+ }
+ return node.getValue();
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
index 321254c..19af85f 100644
--- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
+++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java
@@ -1,15 +1,14 @@
package com.android.server.wifi.hotspot2;
-import static com.android.server.wifi.anqp.Constants.BYTES_IN_EUI48;
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
+import static com.android.server.wifi.hotspot2.anqp.Constants.BYTES_IN_EUI48;
+import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK;
import android.net.wifi.ScanResult;
import android.util.Log;
-import com.android.server.wifi.anqp.ANQPElement;
-import com.android.server.wifi.anqp.Constants;
-import com.android.server.wifi.anqp.RawByteElement;
-import com.android.server.wifi.anqp.VenueNameElement;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+import com.android.server.wifi.hotspot2.anqp.RawByteElement;
import com.android.server.wifi.util.InformationElementUtil;
import java.nio.BufferUnderflowException;
@@ -24,9 +23,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:";
@@ -95,12 +92,9 @@
/*
* From Interworking element:
* mAnt non null indicates the presence of Interworking, i.e. 802.11u
- * mVenueGroup and mVenueType may be null if not present in the Interworking element.
*/
private final Ant mAnt;
private final boolean mInternet;
- private final VenueNameElement.VenueGroup mVenueGroup;
- private final VenueNameElement.VenueType mVenueType;
/*
* From HS20 Indication element:
@@ -256,14 +250,12 @@
mCapacity = bssLoad.capacity;
mAnt = interworking.ant;
mInternet = interworking.internet;
- mVenueGroup = interworking.venueGroup;
- mVenueType = interworking.venueType;
mHSRelease = vsa.hsRelease;
mAnqpDomainID = vsa.anqpDomainID;
mAnqpOICount = roamingConsortium.anqpOICount;
mRoamingConsortiums = roamingConsortium.roamingConsortiums;
mExtendedCapabilities = extendedCapabilities;
- mANQPElements = SupplicantBridge.parseANQPLines(anqpLines);
+ mANQPElements = null;
//set up channel info
mPrimaryFreq = freq;
@@ -303,10 +295,10 @@
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"
+ + (extendedCapabilities.is80211McRTTResponder() ? "Support RTT responder"
: "Do not support RTT responder"));
Log.v("WifiMode", mSSID
+ ", WifiMode: " + InformationElementUtil.WifiMode.toString(mWifiMode)
@@ -339,8 +331,6 @@
mCapacity = base.mCapacity;
mAnt = base.mAnt;
mInternet = base.mInternet;
- mVenueGroup = base.mVenueGroup;
- mVenueType = base.mVenueType;
mHSRelease = base.mHSRelease;
mAnqpDomainID = base.mAnqpDomainID;
mAnqpOICount = base.mAnqpOICount;
@@ -380,9 +370,11 @@
}
public String getTrimmedSSID() {
- for (int n = 0; n < mSSID.length(); n++) {
- if (mSSID.charAt(n) != 0) {
- return mSSID;
+ if (mSSID != null) {
+ for (int n = 0; n < mSSID.length(); n++) {
+ if (mSSID.charAt(n) != 0) {
+ return mSSID;
+ }
}
}
return "";
@@ -420,14 +412,6 @@
return mInternet;
}
- public VenueNameElement.VenueGroup getVenueGroup() {
- return mVenueGroup;
- }
-
- public VenueNameElement.VenueType getVenueType() {
- return mVenueType;
- }
-
public HSRelease getHSRelease() {
return mHSRelease;
}
@@ -452,10 +436,6 @@
return mRoamingConsortiums;
}
- public Long getExtendedCapabilities() {
- return mExtendedCapabilities.extendedCapabilities;
- }
-
public Map<Constants.ANQPElementType, ANQPElement> getANQPElements() {
return mANQPElements;
}
@@ -481,7 +461,7 @@
}
public boolean is80211McResponderSupport() {
- return mExtendedCapabilities.is80211McRTTResponder;
+ return mExtendedCapabilities.is80211McRTTResponder();
}
public boolean isSSID_UTF8() {
@@ -511,11 +491,11 @@
public String toString() {
return String.format("NetworkInfo{SSID='%s', HESSID=%x, BSSID=%x, StationCount=%d, " +
"ChannelUtilization=%d, Capacity=%d, Ant=%s, Internet=%s, " +
- "VenueGroup=%s, VenueType=%s, HSRelease=%s, AnqpDomainID=%d, " +
+ "HSRelease=%s, AnqpDomainID=%d, " +
"AnqpOICount=%d, RoamingConsortiums=%s}",
mSSID, mHESSID, mBSSID, mStationCount,
mChannelUtilization, mCapacity, mAnt, mInternet,
- mVenueGroup, mVenueType, mHSRelease, mAnqpDomainID,
+ mHSRelease, mAnqpDomainID,
mAnqpOICount, Utils.roamingConsortiumsToString(mRoamingConsortiums));
}
diff --git a/service/java/com/android/server/wifi/hotspot2/OMADMAdapter.java b/service/java/com/android/server/wifi/hotspot2/OMADMAdapter.java
deleted file mode 100644
index 9e1a582..0000000
--- a/service/java/com/android/server/wifi/hotspot2/OMADMAdapter.java
+++ /dev/null
@@ -1,593 +0,0 @@
-package com.android.server.wifi.hotspot2;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.net.wifi.WifiManager;
-import android.os.SystemProperties;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.server.wifi.anqp.eap.EAP;
-import com.android.server.wifi.hotspot2.omadm.MOTree;
-import com.android.server.wifi.hotspot2.omadm.OMAConstants;
-import com.android.server.wifi.hotspot2.omadm.OMAConstructed;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import static com.android.server.wifi.anqp.eap.NonEAPInnerAuth.NonEAPType;
-import static com.android.server.wifi.anqp.eap.NonEAPInnerAuth.mapInnerType;
-
-public class OMADMAdapter {
- private final Context mContext;
- private final String mImei;
- private final String mImsi;
- private final String mDevID;
- private final List<PathAccessor> mDevInfo;
- private final List<PathAccessor> mDevDetail;
-
- private static final int IMEI_Length = 14;
-
- private static final String[] ExtWiFiPath = {"DevDetail", "Ext", "org.wi-fi", "Wi-Fi"};
-
- private static final Map<String, String> RTProps = new HashMap<>();
-
- private MOTree mDevInfoTree;
- private MOTree mDevDetailTree;
-
- private static OMADMAdapter sInstance;
-
- static {
- RTProps.put(ExtWiFiPath[2], "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0");
- }
-
- private static abstract class PathAccessor {
- private final String[] mPath;
- private final int mHashCode;
-
- protected PathAccessor(Object ... path) {
- int length = 0;
- for (Object o : path) {
- if (o.getClass() == String[].class) {
- length += ((String[]) o).length;
- }
- else {
- length++;
- }
- }
- mPath = new String[length];
- int n = 0;
- for (Object o : path) {
- if (o.getClass() == String[].class) {
- for (String element : (String[]) o) {
- mPath[n++] = element;
- }
- }
- else if (o.getClass() == Integer.class) {
- mPath[n++] = "x" + o.toString();
- }
- else {
- mPath[n++] = o.toString();
- }
- }
- mHashCode = Arrays.hashCode(mPath);
- }
-
- @Override
- public int hashCode() {
- return mHashCode;
- }
-
- @Override
- public boolean equals(Object thatObject) {
- return thatObject == this || (thatObject instanceof ConstPathAccessor &&
- Arrays.equals(mPath, ((PathAccessor) thatObject).mPath));
- }
-
- private String[] getPath() {
- return mPath;
- }
-
- protected abstract Object getValue();
- }
-
- private static class ConstPathAccessor<T> extends PathAccessor {
- private final T mValue;
-
- protected ConstPathAccessor(T value, Object ... path) {
- super(path);
- mValue = value;
- }
-
- protected Object getValue() {
- return mValue;
- }
- }
-
- public static OMADMAdapter getInstance(Context context) {
- synchronized (OMADMAdapter.class) {
- if (sInstance == null) {
- sInstance = new OMADMAdapter(context);
- }
- return sInstance;
- }
- }
-
- private OMADMAdapter(Context context) {
- mContext = context;
-
- TelephonyManager tm = (TelephonyManager) context
- .getSystemService(Context.TELEPHONY_SERVICE);
- String simOperator = tm.getSimOperator();
- mImsi = tm.getSubscriberId();
- mImei = tm.getImei();
- String strDevId;
-
- /* Use MEID for sprint */
- if ("310120".equals(simOperator) || (mImsi != null && mImsi.startsWith("310120"))) {
- /* MEID is 14 digits. If IMEI is returned as DevId, MEID can be extracted by taking
- * first 14 characters. This is not always true but should be the case for sprint */
- strDevId = tm.getDeviceId().toUpperCase(Locale.US);
- if (strDevId != null && strDevId.length() >= IMEI_Length) {
- strDevId = strDevId.substring(0, IMEI_Length);
- } else {
- Log.w(Utils.hs2LogTag(getClass()),
- "MEID cannot be extracted from DeviceId " + strDevId);
- }
- } else {
- if (isPhoneTypeLTE()) {
- strDevId = mImei;
- } else {
- strDevId = tm.getDeviceId();
- }
- if (strDevId == null) {
- strDevId = "unknown";
- }
- strDevId = strDevId.toUpperCase(Locale.US);
-
- if (!isPhoneTypeLTE()) {
- strDevId = strDevId.substring(0, IMEI_Length);
- }
- }
- mDevID = strDevId;
-
- mDevInfo = new ArrayList<>();
- mDevInfo.add(new ConstPathAccessor<>(strDevId, "DevInfo", "DevID"));
- mDevInfo.add(new ConstPathAccessor<>(getProperty(context, "Man", "ro.product.manufacturer", "unknown"), "DevInfo", "Man"));
- mDevInfo.add(new ConstPathAccessor<>(getProperty(context, "Mod", "ro.product.model", "generic"), "DevInfo", "Mod"));
- mDevInfo.add(new ConstPathAccessor<>(getLocale(context), "DevInfo", "Lang"));
- mDevInfo.add(new ConstPathAccessor<>("1.2", "DevInfo", "DmV"));
-
- mDevDetail = new ArrayList<>();
- mDevDetail.add(new ConstPathAccessor<>(getDeviceType(), "DevDetail", "DevType"));
- mDevDetail.add(new ConstPathAccessor<>(SystemProperties.get("ro.product.brand"), "DevDetail", "OEM"));
- mDevDetail.add(new ConstPathAccessor<>(getVersion(context, false), "DevDetail", "FwV"));
- mDevDetail.add(new ConstPathAccessor<>(getVersion(context, true), "DevDetail", "SwV"));
- mDevDetail.add(new ConstPathAccessor<>(getHwV(), "DevDetail", "HwV"));
- mDevDetail.add(new ConstPathAccessor<>("TRUE", "DevDetail", "LrgObj"));
-
- mDevDetail.add(new ConstPathAccessor<>(32, "DevDetail", "URI", "MaxDepth"));
- mDevDetail.add(new ConstPathAccessor<>(2048, "DevDetail", "URI", "MaxTotLen"));
- mDevDetail.add(new ConstPathAccessor<>(64, "DevDetail", "URI", "MaxSegLen"));
-
- AtomicInteger index = new AtomicInteger(1);
- mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_TTLS, ExtWiFiPath, "EAPMethodList", index, "EAPType"));
- mDevDetail.add(new ConstPathAccessor<>(mapInnerType(NonEAPType.MSCHAPv2), ExtWiFiPath, "EAPMethodList", index, "InnerMethod"));
-
- index.incrementAndGet();
- mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_TTLS, ExtWiFiPath, "EAPMethodList", index, "EAPType"));
- mDevDetail.add(new ConstPathAccessor<>(mapInnerType(NonEAPType.PAP), ExtWiFiPath, "EAPMethodList", index, "InnerMethod"));
-
- index.incrementAndGet();
- mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_TTLS, ExtWiFiPath, "EAPMethodList", index, "EAPType"));
- mDevDetail.add(new ConstPathAccessor<>(mapInnerType(NonEAPType.MSCHAP), ExtWiFiPath, "EAPMethodList", index, "InnerMethod"));
-
- index.incrementAndGet();
- mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_TLS, ExtWiFiPath, "EAPMethodList", index, "EAPType"));
- index.incrementAndGet();
- mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_AKA, ExtWiFiPath, "EAPMethodList", index, "EAPType"));
- index.incrementAndGet();
- mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_AKAPrim, ExtWiFiPath, "EAPMethodList", index, "EAPType"));
- index.incrementAndGet();
- mDevDetail.add(new ConstPathAccessor<>(EAP.EAP_SIM, ExtWiFiPath, "EAPMethodList", index, "EAPType"));
-
- mDevDetail.add(new ConstPathAccessor<>("FALSE", ExtWiFiPath, "ManufacturingCertificate"));
- mDevDetail.add(new ConstPathAccessor<>(mImsi, ExtWiFiPath, "IMSI"));
- mDevDetail.add(new ConstPathAccessor<>(mImei, ExtWiFiPath, "IMEI_MEID"));
- mDevDetail.add(new PathAccessor(ExtWiFiPath, "Wi-FiMACAddress") {
- @Override
- protected String getValue() {
- return getMAC();
- }
- });
- }
-
- private static void buildNode(PathAccessor pathAccessor, int depth, OMAConstructed parent)
- throws IOException {
- String[] path = pathAccessor.getPath();
- String name = path[depth];
- if (depth < path.length - 1) {
- OMAConstructed node = (OMAConstructed) parent.getChild(name);
- if (node == null) {
- node = (OMAConstructed) parent.addChild(name, RTProps.get(name),
- null, null);
- }
- buildNode(pathAccessor, depth + 1, node);
- }
- else if (pathAccessor.getValue() != null) {
- parent.addChild(name, null, pathAccessor.getValue().toString(), null);
- }
- }
-
- public String getMAC() {
- WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
- return wifiManager != null ?
- String.format("%012x",
- Utils.parseMac(wifiManager.getConnectionInfo().getMacAddress())) :
- null;
- }
-
- public String getImei() {
- return mImei;
- }
-
- public byte[] getMeid() {
- return Arrays.copyOf(mImei.getBytes(StandardCharsets.ISO_8859_1), IMEI_Length);
- }
-
- public String getDevID() {
- return mDevID;
- }
-
- public MOTree getMO(String urn) {
- try {
- switch (urn) {
- case OMAConstants.DevInfoURN:
- if (mDevInfoTree == null) {
- OMAConstructed root = new OMAConstructed(null, "DevInfo", urn);
- for (PathAccessor pathAccessor : mDevInfo) {
- buildNode(pathAccessor, 1, root);
- }
- mDevInfoTree = MOTree.buildMgmtTree(OMAConstants.DevInfoURN,
- OMAConstants.OMAVersion, root);
- }
- return mDevInfoTree;
- case OMAConstants.DevDetailURN:
- if (mDevDetailTree == null) {
- OMAConstructed root = new OMAConstructed(null, "DevDetail", urn);
- for (PathAccessor pathAccessor : mDevDetail) {
- buildNode(pathAccessor, 1, root);
- }
- mDevDetailTree = MOTree.buildMgmtTree(OMAConstants.DevDetailURN,
- OMAConstants.OMAVersion, root);
- }
- return mDevDetailTree;
- default:
- throw new IllegalArgumentException(urn);
- }
- }
- catch (IOException ioe) {
- Log.e(Utils.hs2LogTag(getClass()), "Caught exception building OMA Tree: " + ioe, ioe);
- return null;
- }
-
- /*
- switch (urn) {
- case DevInfoURN: return DevInfo;
- case DevDetailURN: return DevDetail;
- default: throw new IllegalArgumentException(urn);
- }
- */
- }
-
- // TODO: For now, assume the device supports LTE.
- private static boolean isPhoneTypeLTE() {
- return true;
- }
-
- private static String getHwV() {
- try {
- return SystemProperties.get("ro.hardware", "Unknown")
- + "." + SystemProperties.get("ro.revision", "Unknown");
- } catch (RuntimeException e) {
- return "Unknown";
- }
- }
-
- private static String getDeviceType() {
- String devicetype = SystemProperties.get("ro.build.characteristics");
- if ((((TextUtils.isEmpty(devicetype)) || (!devicetype.equals("tablet"))))) {
- devicetype = "phone";
- }
- return devicetype;
- }
-
- private static String getVersion(Context context, boolean swv) {
- String version;
- try {
- if (!isSprint(context) && swv) {
- return "Android " + SystemProperties.get("ro.build.version.release");
- } else {
- version = SystemProperties.get("ro.build.version.full");
- if (null == version || version.equals("")) {
- return SystemProperties.get("ro.build.id", null) + "~"
- + SystemProperties.get("ro.build.config.version", null) + "~"
- + SystemProperties.get("gsm.version.baseband", null) + "~"
- + SystemProperties.get("ro.gsm.flexversion", null);
- }
- }
- } catch (RuntimeException e) {
- return "Unknown";
- }
- return version;
- }
-
- private static boolean isSprint(Context context) {
- TelephonyManager tm = (TelephonyManager) context
- .getSystemService(Context.TELEPHONY_SERVICE);
- String simOperator = tm.getSimOperator();
- String imsi = tm.getSubscriberId();
- /* Use MEID for sprint */
- if ("310120".equals(simOperator) || (imsi != null && imsi.startsWith("310120"))) {
- return true;
- } else {
- return false;
- }
- }
-
- private static String getLocale(Context context) {
- String strLang = readValueFromFile(context, "Lang");
- if (strLang == null) {
- strLang = Locale.getDefault().toString();
- }
- return strLang;
- }
-
- private static String getProperty(Context context, String key, String propKey, String dflt) {
- String strMan = readValueFromFile(context, key);
- if (strMan == null) {
- strMan = SystemProperties.get(propKey, dflt);
- }
- return strMan;
- }
-
- private static String readValueFromFile(Context context, String propName) {
- String ret = null;
- // use preference instead of the system property
- SharedPreferences prefs = context.getSharedPreferences("dmconfig", 0);
- if (prefs.contains(propName)) {
- ret = prefs.getString(propName, "");
- if (ret.length() == 0) {
- ret = null;
- }
- }
- return ret;
- }
-
- private static final String DevDetail =
- "<MgmtTree>" +
- "<VerDTD>1.2</VerDTD>" +
- "<Node>" +
- "<NodeName>DevDetail</NodeName>" +
- "<RTProperties>" +
- "<Type>" +
- "<DDFName>urn:oma:mo:oma-dm-devdetail:1.0</DDFName>" +
- "</Type>" +
- "</RTProperties>" +
- "<Node>" +
- "<NodeName>Ext</NodeName>" +
- "<Node>" +
- "<NodeName>org.wi-fi</NodeName>" +
- "<RTProperties>" +
- "<Type>" +
- "<DDFName>" +
- "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext :1.0" +
- "</DDFName>" +
- "</Type>" +
- "</RTProperties>" +
- "<Node>" +
- "<NodeName>Wi-Fi</NodeName>" +
- "<Node>" +
- "<NodeName>EAPMethodList</NodeName>" +
- "<Node>" +
- "<NodeName>Method01</NodeName>" +
- "<!-- EAP-TTLS/MS-CHAPv2 -->" +
- "<Node>" +
- "<NodeName>EAPType</NodeName>" +
- "<Value>21</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>InnerMethod</NodeName>" +
- "<Value>MS-CHAP-V2</Value>" +
- "</Node>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Method02</NodeName>" +
- "<!-- EAP-TLS -->" +
- "<Node>" +
- "<NodeName>EAPType</NodeName>" +
- "<Value>13</Value>" +
- "</Node>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Method03</NodeName>" +
- "<!-- EAP-SIM -->" +
- "<Node>" +
- "<NodeName>EAPType</NodeName>" +
- "<Value>18</Value>" +
- "</Node>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Method04</NodeName>" +
- "<!-- EAP-AKA -->" +
- "<Node>" +
- "<NodeName>EAPType</NodeName>" +
- "<Value>23</Value>" +
- "</Node>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Method05</NodeName>" +
- "<!-- EAP-AKA' -->" +
- "<Node>" +
- "<NodeName>EAPType</NodeName>" +
- "<Value>50</Value>" +
- "</Node>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Method06</NodeName>" +
- "<!-- Supported method (EAP-TTLS/PAP) not mandated by Hotspot2.0-->" +
- "<Node>" +
- "<NodeName>EAPType</NodeName>" +
- "<Value>21</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>InnerMethod</NodeName>" +
- "<Value>PAP</Value>" +
- "</Node>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Method07</NodeName>" +
- "<!-- Supported method (PEAP/EAP-GTC) not mandated by Hotspot 2.0-->" +
- "<Node>" +
- "<NodeName>EAPType</NodeName>" +
- "<Value>25</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>InnerEAPType</NodeName>" +
- "<Value>6</Value>" +
- "</Node>" +
- "</Node>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>SPCertificate</NodeName>" +
- "<Node>" +
- "<NodeName>Cert01</NodeName>" +
- "<Node>" +
- "<NodeName>CertificateIssuerName</NodeName>" +
- "<Value>CN=RuckusCA</Value>" +
- "</Node>" +
- "</Node>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>ManufacturingCertificate</NodeName>" +
- "<Value>FALSE</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Wi-FiMACAddress</NodeName>" +
- "<Value>001d2e112233</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>ClientTriggerRedirectURI</NodeName>" +
- "<Value>http://127.0.0.1:12345/index.htm</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Ops</NodeName>" +
- "<Node>" +
- "<NodeName>launchBrowserToURI</NodeName>" +
- "<Value></Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>negotiateClientCertTLS</NodeName>" +
- "<Value></Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>getCertificate</NodeName>" +
- "<Value></Value>" +
- "</Node>" +
- "</Node>" +
- "</Node>" +
- "<!-- End of Wi-Fi node -->" +
- "</Node>" +
- "<!-- End of org.wi-fi node -->" +
- "</Node>" +
- "<!-- End of Ext node -->" +
- "<Node>" +
- "<NodeName>URI</NodeName>" +
- "<Node>" +
- "<NodeName>MaxDepth</NodeName>" +
- "<Value>32</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>MaxTotLen</NodeName>" +
- "<Value>2048</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>MaxSegLen</NodeName>" +
- "<Value>64</Value>" +
- "</Node>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>DevType</NodeName>" +
- "<Value>Smartphone</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>OEM</NodeName>" +
- "<Value>ACME</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>FwV</NodeName>" +
- "<Value>1.2.100.5</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>SwV</NodeName>" +
- "<Value>9.11.130</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>HwV</NodeName>" +
- "<Value>1.0</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>LrgObj</NodeName>" +
- "<Value>TRUE</Value>" +
- "</Node>" +
- "</Node>" +
- "</MgmtTree>";
-
-
- private static final String DevInfo =
- "<MgmtTree>" +
- "<VerDTD>1.2</VerDTD>" +
- "<Node>" +
- "<NodeName>DevInfo</NodeName>" +
- "<RTProperties>" +
- "<Type>" +
- "<DDFName>urn:oma:mo:oma-dm-devinfo:1.0" +
- "</DDFName>" +
- "</Type>" +
- "</RTProperties>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>DevID</NodeName>" +
- "<Path>DevInfo</Path>" +
- "<Value>urn:acme:00-11-22-33-44-55</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Man</NodeName>" +
- "<Path>DevInfo</Path>" +
- "<Value>ACME</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Mod</NodeName>" +
- "<Path>DevInfo</Path>" +
- "<Value>HS2.0-01</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>DmV</NodeName>" +
- "<Path>DevInfo</Path>" +
- "<Value>1.2</Value>" +
- "</Node>" +
- "<Node>" +
- "<NodeName>Lang</NodeName>" +
- "<Path>DevInfo</Path>" +
- "<Value>en-US</Value>" +
- "</Node>" +
- "</MgmtTree>";
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/OWNERS b/service/java/com/android/server/wifi/hotspot2/OWNERS
new file mode 100644
index 0000000..bea5780
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/OWNERS
@@ -0,0 +1 @@
+zqiu@google.com
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointConfigStoreData.java b/service/java/com/android/server/wifi/hotspot2/PasspointConfigStoreData.java
new file mode 100644
index 0000000..74a4760
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointConfigStoreData.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.text.TextUtils;
+
+import com.android.internal.util.XmlUtils;
+import com.android.server.wifi.SIMAccessor;
+import com.android.server.wifi.WifiConfigStore;
+import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.util.XmlUtil;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible for Passpoint specific configuration store data. There are two types of
+ * configuration data, system wide and user specific. The system wide configurations are stored
+ * in the share store and user specific configurations are store in the user store.
+ *
+ * Below are the current configuration data for each respective store file, the list will
+ * probably grow in the future.
+ *
+ * Share Store (system wide configurations)
+ * - Current provider index - use for assigning provider ID during provider creation, to make
+ * sure each provider will have an unique ID across all users.
+ *
+ * User Store (user specific configurations)
+ * - Provider list - list of Passpoint provider configurations
+ *
+ */
+public class PasspointConfigStoreData implements WifiConfigStore.StoreData {
+ private static final String XML_TAG_SECTION_HEADER_PASSPOINT_CONFIG_DATA =
+ "PasspointConfigData";
+ private static final String XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST =
+ "ProviderList";
+ private static final String XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER =
+ "Provider";
+ private static final String XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION =
+ "Configuration";
+
+ private static final String XML_TAG_PROVIDER_ID = "ProviderID";
+ private static final String XML_TAG_CREATOR_UID = "CreatorUID";
+ private static final String XML_TAG_CA_CERTIFICATE_ALIAS = "CaCertificateAlias";
+ private static final String XML_TAG_CLIENT_CERTIFICATE_ALIAS = "ClientCertificateAlias";
+ private static final String XML_TAG_CLIENT_PRIVATE_KEY_ALIAS = "ClientPrivateKeyAlias";
+
+ private static final String XML_TAG_PROVIDER_INDEX = "ProviderIndex";
+
+ private final WifiKeyStore mKeyStore;
+ private final SIMAccessor mSimAccessor;
+ private final DataSource mDataSource;
+
+ /**
+ * Interface define the data source for the Passpoint configuration store data.
+ */
+ public interface DataSource {
+ /**
+ * Retrieve the provider list from the data source.
+ *
+ * @return List of {@link PasspointProvider}
+ */
+ List<PasspointProvider> getProviders();
+
+ /**
+ * Set the provider list in the data source.
+ *
+ * @param providers The list of providers
+ */
+ void setProviders(List<PasspointProvider> providers);
+
+ /**
+ * Retrieve the current provider index.
+ *
+ * @return long
+ */
+ long getProviderIndex();
+
+ /**
+ * Set the current provider index.
+ *
+ * @param providerIndex The provider index used for provider creation
+ */
+ void setProviderIndex(long providerIndex);
+ }
+
+ PasspointConfigStoreData(WifiKeyStore keyStore, SIMAccessor simAccessor,
+ DataSource dataSource) {
+ mKeyStore = keyStore;
+ mSimAccessor = simAccessor;
+ mDataSource = dataSource;
+ }
+
+ @Override
+ public void serializeData(XmlSerializer out, boolean shared)
+ throws XmlPullParserException, IOException {
+ if (shared) {
+ serializeShareData(out);
+ } else {
+ serializeUserData(out);
+ }
+ }
+
+ @Override
+ public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
+ throws XmlPullParserException, IOException {
+ if (shared) {
+ deserializeShareData(in, outerTagDepth);
+ } else {
+ deserializeUserData(in, outerTagDepth);
+ }
+ }
+
+ @Override
+ public void resetData(boolean shared) {
+ if (shared) {
+ resetShareData();
+ } else {
+ resetUserData();
+ }
+ }
+
+ @Override
+ public String getName() {
+ return XML_TAG_SECTION_HEADER_PASSPOINT_CONFIG_DATA;
+ }
+
+ @Override
+ public boolean supportShareData() {
+ return true;
+ }
+
+ /**
+ * Serialize share data (system wide Passpoint configurations) to a XML block.
+ *
+ * @param out The output stream to serialize data to
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private void serializeShareData(XmlSerializer out) throws XmlPullParserException, IOException {
+ XmlUtil.writeNextValue(out, XML_TAG_PROVIDER_INDEX, mDataSource.getProviderIndex());
+ }
+
+ /**
+ * Serialize user data (user specific Passpoint configurations) to a XML block.
+ *
+ * @param out The output stream to serialize data to
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private void serializeUserData(XmlSerializer out) throws XmlPullParserException, IOException {
+ serializeProviderList(out, mDataSource.getProviders());
+ }
+
+ /**
+ * Serialize the list of Passpoint providers from the data source to a XML block.
+ *
+ * @param out The output stream to serialize data to
+ * @param providerList The list of providers to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private void serializeProviderList(XmlSerializer out, List<PasspointProvider> providerList)
+ throws XmlPullParserException, IOException {
+ if (providerList == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST);
+ for (PasspointProvider provider : providerList) {
+ serializeProvider(out, provider);
+ }
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST);
+ }
+
+ /**
+ * Serialize a Passpoint provider to a XML block.
+ *
+ * @param out The output stream to serialize data to
+ * @param provider The provider to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private void serializeProvider(XmlSerializer out, PasspointProvider provider)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER);
+ XmlUtil.writeNextValue(out, XML_TAG_PROVIDER_ID, provider.getProviderId());
+ XmlUtil.writeNextValue(out, XML_TAG_CREATOR_UID, provider.getCreatorUid());
+ XmlUtil.writeNextValue(out, XML_TAG_CA_CERTIFICATE_ALIAS,
+ provider.getCaCertificateAlias());
+ XmlUtil.writeNextValue(out, XML_TAG_CLIENT_CERTIFICATE_ALIAS,
+ provider.getClientCertificateAlias());
+ XmlUtil.writeNextValue(out, XML_TAG_CLIENT_PRIVATE_KEY_ALIAS,
+ provider.getClientPrivateKeyAlias());
+ if (provider.getConfig() != null) {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION);
+ PasspointXmlUtils.serializePasspointConfiguration(out, provider.getConfig());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION);
+ }
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER);
+ }
+
+ /**
+ * Deserialize share data (system wide Passpoint configurations) from the input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private void deserializeShareData(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ 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_PROVIDER_INDEX:
+ mDataSource.setProviderIndex((long) value);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown value under share store data "
+ + valueName[0]);
+ }
+ }
+ }
+
+ /**
+ * Deserialize user data (user specific Passpoint configurations) from the input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private void deserializeUserData(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ String[] headerName = new String[1];
+ while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) {
+ switch (headerName[0]) {
+ case XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER_LIST:
+ mDataSource.setProviders(deserializeProviderList(in, outerTagDepth + 1));
+ break;
+ default:
+ throw new XmlPullParserException("Unknown Passpoint user store data "
+ + headerName[0]);
+ }
+ }
+ }
+
+ /**
+ * Deserialize a list of Passpoint providers from the input stream.
+ *
+ * @param in The input stream to read data form
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return List of {@link PasspointProvider}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private List<PasspointProvider> deserializeProviderList(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ List<PasspointProvider> providerList = new ArrayList<>();
+ while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_PASSPOINT_PROVIDER,
+ outerTagDepth)) {
+ providerList.add(deserializeProvider(in, outerTagDepth + 1));
+ }
+ return providerList;
+ }
+
+ /**
+ * Deserialize a Passpoint provider from the input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link PasspointProvider}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private PasspointProvider deserializeProvider(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ long providerId = Long.MIN_VALUE;
+ int creatorUid = Integer.MIN_VALUE;
+ String caCertificateAlias = null;
+ String clientCertificateAlias = null;
+ String clientPrivateKeyAlias = null;
+ PasspointConfiguration config = null;
+ while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
+ if (in.getAttributeValue(null, "name") != null) {
+ // Value elements.
+ String[] name = new String[1];
+ Object value = XmlUtil.readCurrentValue(in, name);
+ switch (name[0]) {
+ case XML_TAG_PROVIDER_ID:
+ providerId = (long) value;
+ break;
+ case XML_TAG_CREATOR_UID:
+ creatorUid = (int) value;
+ break;
+ case XML_TAG_CA_CERTIFICATE_ALIAS:
+ caCertificateAlias = (String) value;
+ break;
+ case XML_TAG_CLIENT_CERTIFICATE_ALIAS:
+ clientCertificateAlias = (String) value;
+ break;
+ case XML_TAG_CLIENT_PRIVATE_KEY_ALIAS:
+ clientPrivateKeyAlias = (String) value;
+ break;
+ }
+ } else {
+ if (!TextUtils.equals(in.getName(),
+ XML_TAG_SECTION_HEADER_PASSPOINT_CONFIGURATION)) {
+ throw new XmlPullParserException("Unexpected section under Provider: "
+ + in.getName());
+ }
+ config = PasspointXmlUtils.deserializePasspointConfiguration(in,
+ outerTagDepth + 1);
+ }
+ }
+ if (providerId == Long.MIN_VALUE) {
+ throw new XmlPullParserException("Missing provider ID");
+ }
+ if (config == null) {
+ throw new XmlPullParserException("Missing Passpoint configuration");
+ }
+ return new PasspointProvider(config, mKeyStore, mSimAccessor, providerId, creatorUid,
+ caCertificateAlias, clientCertificateAlias, clientPrivateKeyAlias);
+ }
+
+ /**
+ * Reset share data (system wide Passpoint configurations).
+ */
+ private void resetShareData() {
+ mDataSource.setProviderIndex(0);
+ }
+
+ /**
+ * Reset user data (user specific Passpoint configurations).
+ */
+ private void resetUserData() {
+ mDataSource.setProviders(new ArrayList<PasspointProvider>());
+ }
+}
+
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointEventHandler.java b/service/java/com/android/server/wifi/hotspot2/PasspointEventHandler.java
new file mode 100644
index 0000000..6a7d0af
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointEventHandler.java
@@ -0,0 +1,159 @@
+/*
+ * 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.hotspot2;
+
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class handles passpoint specific interactions with the AP, such as ANQP
+ * elements requests, passpoint icon requests, and wireless network management
+ * event notifications.
+ */
+public class PasspointEventHandler {
+ private final WifiNative mSupplicantHook;
+ private final Callbacks mCallbacks;
+
+ /**
+ * Interface to be implemented by the client to receive callbacks for passpoint
+ * related events.
+ */
+ public interface Callbacks {
+ /**
+ * Invoked on received of ANQP response. |anqpElements| will be null on failure.
+ * @param bssid BSSID of the AP
+ * @param anqpElements ANQP elements to be queried
+ */
+ void onANQPResponse(long bssid,
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements);
+
+ /**
+ * Invoked on received of icon response. |filename| and |data| will be null
+ * on failure.
+ * @param bssid BSSID of the AP
+ * @param filename Name of the icon file
+ * @data icon data bytes
+ */
+ void onIconResponse(long bssid, String filename, byte[] data);
+
+ /**
+ * Invoked on received of Hotspot 2.0 Wireless Network Management frame.
+ * @param data Wireless Network Management frame data
+ */
+ void onWnmFrameReceived(WnmData data);
+ }
+
+ public PasspointEventHandler(WifiNative supplicantHook, Callbacks callbacks) {
+ mSupplicantHook = supplicantHook;
+ mCallbacks = callbacks;
+ }
+
+ /**
+ * Request the specified ANQP elements |elements| from the specified AP |bssid|.
+ * @param bssid BSSID of the AP
+ * @param elements ANQP elements to be queried
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean requestANQP(long bssid, List<Constants.ANQPElementType> elements) {
+ Pair<Set<Integer>, Set<Integer>> querySets = buildAnqpIdSet(elements);
+ if (bssid == 0 || querySets == null) return false;
+ if (!mSupplicantHook.requestAnqp(
+ Utils.macToString(bssid), querySets.first, querySets.second)) {
+ Log.d(Utils.hs2LogTag(getClass()), "ANQP failed on " + Utils.macToString(bssid));
+ return false;
+ }
+ Log.d(Utils.hs2LogTag(getClass()), "ANQP initiated on " + Utils.macToString(bssid));
+ return true;
+ }
+
+ /**
+ * Request a passpoint icon file |filename| from the specified AP |bssid|.
+ * @param bssid BSSID of the AP
+ * @param fileName name of the icon file
+ * @return true if request is sent successfully, false otherwise
+ */
+ public boolean requestIcon(long bssid, String fileName) {
+ if (bssid == 0 || fileName == null) return false;
+ return mSupplicantHook.requestIcon(Utils.macToString(bssid), fileName);
+ }
+
+ /**
+ * Invoked when ANQP query is completed.
+ * TODO(zqiu): currently ANQP completion notification is through WifiMonitor,
+ * this shouldn't be needed once we switch over to wificond for ANQP requests.
+ * @param anqpEvent ANQP result data retrieved. ANQP elements could be empty in the event to
+ * indicate any failures.
+ */
+ public void notifyANQPDone(AnqpEvent anqpEvent) {
+ if (anqpEvent == null) return;
+ mCallbacks.onANQPResponse(anqpEvent.getBssid(), anqpEvent.getElements());
+ }
+
+ /**
+ * Invoked when icon query is completed.
+ * TODO(zqiu): currently icon completion notification is through WifiMonitor,
+ * this shouldn't be needed once we switch over to wificond for icon requests.
+ * @param iconEvent icon event data
+ */
+ public void notifyIconDone(IconEvent iconEvent) {
+ if (iconEvent == null) return;
+ mCallbacks.onIconResponse(
+ iconEvent.getBSSID(), iconEvent.getFileName(), iconEvent.getData());
+ }
+
+ /**
+ * Invoked when a Wireless Network Management (WNM) frame is received.
+ * TODO(zqiu): currently WNM frame notification is through WifiMonitor,
+ * this shouldn't be needed once we switch over to wificond for WNM frame monitoring.
+ * @param data WNM frame data
+ */
+ public void notifyWnmFrameReceived(WnmData data) {
+ mCallbacks.onWnmFrameReceived(data);
+ }
+
+ /**
+ * Create the set of ANQP ID's to query.
+ *
+ * @param querySet elements to query
+ * @return Pair of <set of ANQP ID's, set of HS20 subtypes>
+ */
+ private static Pair<Set<Integer>, Set<Integer>> buildAnqpIdSet(
+ List<Constants.ANQPElementType> querySet) {
+ Set<Integer> anqpIds = new HashSet<>();
+ Set<Integer> hs20Subtypes = new HashSet<>();
+ for (Constants.ANQPElementType elementType : querySet) {
+ Integer id = Constants.getANQPElementID(elementType);
+ if (id != null) {
+ anqpIds.add(id);
+ } else {
+ id = Constants.getHS20ElementID(elementType);
+ hs20Subtypes.add(id);
+ }
+ }
+ return Pair.create(anqpIds, hs20Subtypes);
+ }
+
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
new file mode 100644
index 0000000..9000d43
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
@@ -0,0 +1,551 @@
+/*
+ * 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.hotspot2;
+
+import static android.net.wifi.WifiManager.ACTION_PASSPOINT_DEAUTH_IMMINENT;
+import static android.net.wifi.WifiManager.ACTION_PASSPOINT_ICON;
+import static android.net.wifi.WifiManager.ACTION_PASSPOINT_OSU_PROVIDERS_LIST;
+import static android.net.wifi.WifiManager.ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION;
+import static android.net.wifi.WifiManager.EXTRA_ANQP_ELEMENT_DATA;
+import static android.net.wifi.WifiManager.EXTRA_BSSID_LONG;
+import static android.net.wifi.WifiManager.EXTRA_DELAY;
+import static android.net.wifi.WifiManager.EXTRA_ESS;
+import static android.net.wifi.WifiManager.EXTRA_FILENAME;
+import static android.net.wifi.WifiManager.EXTRA_ICON;
+import static android.net.wifi.WifiManager.EXTRA_SUBSCRIPTION_REMEDIATION_METHOD;
+import static android.net.wifi.WifiManager.EXTRA_URL;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.SIMAccessor;
+import com.android.server.wifi.WifiConfigManager;
+import com.android.server.wifi.WifiConfigStore;
+import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+import com.android.server.wifi.hotspot2.anqp.RawByteElement;
+import com.android.server.wifi.util.InformationElementUtil;
+import com.android.server.wifi.util.ScanResultUtil;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class provides the APIs to manage Passpoint provider configurations.
+ * It deals with the following:
+ * - Maintaining a list of configured Passpoint providers for provider matching.
+ * - Persisting the providers configurations to store when required.
+ * - matching Passpoint providers based on the scan results
+ * - Supporting WifiManager Public API calls:
+ * > addOrUpdatePasspointConfiguration()
+ * > removePasspointConfiguration()
+ * > getPasspointConfigurations()
+ *
+ * The provider matching requires obtaining additional information from the AP (ANQP elements).
+ * The ANQP elements will be cached using {@link AnqpCache} to avoid unnecessary requests.
+ *
+ * NOTE: These API's are not thread safe and should only be used from WifiStateMachine thread.
+ */
+public class PasspointManager {
+ private static final String TAG = "PasspointManager";
+
+ /**
+ * Handle for the current {@link PasspointManager} instance. This is needed to avoid
+ * circular dependency with the WifiConfigManger, it will be used for adding the
+ * legacy Passpoint configurations.
+ *
+ * This can be eliminated once we can remove the dependency for WifiConfigManager (for
+ * triggering config store write) from this class.
+ */
+ private static PasspointManager sPasspointManager;
+
+ private final PasspointEventHandler mHandler;
+ private final SIMAccessor mSimAccessor;
+ private final WifiKeyStore mKeyStore;
+ private final PasspointObjectFactory mObjectFactory;
+ private final Map<String, PasspointProvider> mProviders;
+ private final AnqpCache mAnqpCache;
+ private final ANQPRequestManager mAnqpRequestManager;
+ private final WifiConfigManager mWifiConfigManager;
+ private final CertificateVerifier mCertVerifier;
+
+ // Counter used for assigning unique identifier to each provider.
+ private long mProviderIndex;
+
+ private class CallbackHandler implements PasspointEventHandler.Callbacks {
+ private final Context mContext;
+ CallbackHandler(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void onANQPResponse(long bssid,
+ Map<Constants.ANQPElementType, ANQPElement> anqpElements) {
+ // Notify request manager for the completion of a request.
+ ANQPNetworkKey anqpKey =
+ mAnqpRequestManager.onRequestCompleted(bssid, anqpElements != null);
+ if (anqpElements == null || anqpKey == null) {
+ // Query failed or the request wasn't originated from us (not tracked by the
+ // request manager). Nothing to be done.
+ return;
+ }
+
+ // Add new entry to the cache.
+ mAnqpCache.addEntry(anqpKey, anqpElements);
+
+ // Broadcast OSU providers info.
+ if (anqpElements.containsKey(Constants.ANQPElementType.HSOSUProviders)) {
+ RawByteElement osuProviders = (RawByteElement) anqpElements.get(
+ Constants.ANQPElementType.HSOSUProviders);
+ Intent intent = new Intent(ACTION_PASSPOINT_OSU_PROVIDERS_LIST);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(EXTRA_BSSID_LONG, bssid);
+ intent.putExtra(EXTRA_ANQP_ELEMENT_DATA, osuProviders.getPayload());
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.ACCESS_WIFI_STATE);
+ }
+ }
+
+ @Override
+ public void onIconResponse(long bssid, String fileName, byte[] data) {
+ Intent intent = new Intent(ACTION_PASSPOINT_ICON);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(EXTRA_BSSID_LONG, bssid);
+ intent.putExtra(EXTRA_FILENAME, fileName);
+ if (data != null) {
+ intent.putExtra(EXTRA_ICON, Icon.createWithData(data, 0, data.length));
+ }
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.ACCESS_WIFI_STATE);
+ }
+
+ @Override
+ public void onWnmFrameReceived(WnmData event) {
+ // %012x HS20-SUBSCRIPTION-REMEDIATION "%u %s", osu_method, url
+ // %012x HS20-DEAUTH-IMMINENT-NOTICE "%u %u %s", code, reauth_delay, url
+ Intent intent;
+ if (event.isDeauthEvent()) {
+ intent = new Intent(ACTION_PASSPOINT_DEAUTH_IMMINENT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(EXTRA_BSSID_LONG, event.getBssid());
+ intent.putExtra(EXTRA_URL, event.getUrl());
+ intent.putExtra(EXTRA_ESS, event.isEss());
+ intent.putExtra(EXTRA_DELAY, event.getDelay());
+ } else {
+ intent = new Intent(ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(EXTRA_BSSID_LONG, event.getBssid());
+ intent.putExtra(EXTRA_SUBSCRIPTION_REMEDIATION_METHOD, event.getMethod());
+ intent.putExtra(EXTRA_URL, event.getUrl());
+ }
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.ACCESS_WIFI_STATE);
+ }
+ }
+
+ /**
+ * Data provider for the Passpoint configuration store data {@link PasspointConfigStoreData}.
+ */
+ private class DataSourceHandler implements PasspointConfigStoreData.DataSource {
+ @Override
+ public List<PasspointProvider> getProviders() {
+ List<PasspointProvider> providers = new ArrayList<>();
+ for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) {
+ providers.add(entry.getValue());
+ }
+ return providers;
+ }
+
+ @Override
+ public void setProviders(List<PasspointProvider> providers) {
+ mProviders.clear();
+ for (PasspointProvider provider : providers) {
+ mProviders.put(provider.getConfig().getHomeSp().getFqdn(), provider);
+ }
+ }
+
+ @Override
+ public long getProviderIndex() {
+ return mProviderIndex;
+ }
+
+ @Override
+ public void setProviderIndex(long providerIndex) {
+ mProviderIndex = providerIndex;
+ }
+ }
+
+ public PasspointManager(Context context, WifiNative wifiNative, WifiKeyStore keyStore,
+ Clock clock, SIMAccessor simAccessor, PasspointObjectFactory objectFactory,
+ WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore) {
+ mHandler = objectFactory.makePasspointEventHandler(wifiNative,
+ new CallbackHandler(context));
+ mKeyStore = keyStore;
+ mSimAccessor = simAccessor;
+ mObjectFactory = objectFactory;
+ mProviders = new HashMap<>();
+ mAnqpCache = objectFactory.makeAnqpCache(clock);
+ mAnqpRequestManager = objectFactory.makeANQPRequestManager(mHandler, clock);
+ mCertVerifier = objectFactory.makeCertificateVerifier();
+ mWifiConfigManager = wifiConfigManager;
+ mProviderIndex = 0;
+ wifiConfigStore.registerStoreData(objectFactory.makePasspointConfigStoreData(
+ mKeyStore, mSimAccessor, new DataSourceHandler()));
+ sPasspointManager = this;
+ }
+
+ /**
+ * Add or update a Passpoint provider with the given configuration.
+ *
+ * Each provider is uniquely identified by its FQDN (Fully Qualified Domain Name).
+ * In the case when there is an existing configuration with the same FQDN
+ * a provider with the new configuration will replace the existing provider.
+ *
+ * @param config Configuration of the Passpoint provider to be added
+ * @return true if provider is added, false otherwise
+ */
+ public boolean addOrUpdateProvider(PasspointConfiguration config, int uid) {
+ if (config == null) {
+ Log.e(TAG, "Configuration not provided");
+ return false;
+ }
+ if (!config.validate()) {
+ Log.e(TAG, "Invalid configuration");
+ return false;
+ }
+
+ // For Hotspot 2.0 Release 1, the CA Certificate must be trusted by one of the pre-loaded
+ // public CAs in the system key store on the device. Since the provisioning method
+ // for Release 1 is not standardized nor trusted, this is a reasonable restriction
+ // to improve security. The presence of UpdateIdentifier is used to differentiate
+ // between R1 and R2 configuration.
+ if (config.getUpdateIdentifier() == Integer.MIN_VALUE
+ && config.getCredential().getCaCertificate() != null) {
+ try {
+ mCertVerifier.verifyCaCert(config.getCredential().getCaCertificate());
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to verify CA certificate: " + e.getMessage());
+ return false;
+ }
+ }
+
+ // Create a provider and install the necessary certificates and keys.
+ PasspointProvider newProvider = mObjectFactory.makePasspointProvider(
+ config, mKeyStore, mSimAccessor, mProviderIndex++, uid);
+
+ if (!newProvider.installCertsAndKeys()) {
+ Log.e(TAG, "Failed to install certificates and keys to keystore");
+ return false;
+ }
+
+ // Remove existing provider with the same FQDN.
+ if (mProviders.containsKey(config.getHomeSp().getFqdn())) {
+ Log.d(TAG, "Replacing configuration for " + config.getHomeSp().getFqdn());
+ mProviders.get(config.getHomeSp().getFqdn()).uninstallCertsAndKeys();
+ mProviders.remove(config.getHomeSp().getFqdn());
+ }
+
+ mProviders.put(config.getHomeSp().getFqdn(), newProvider);
+ mWifiConfigManager.saveToStore(true /* forceWrite */);
+ Log.d(TAG, "Added/updated Passpoint configuration: " + config.getHomeSp().getFqdn()
+ + " by " + uid);
+ return true;
+ }
+
+ /**
+ * Remove a Passpoint provider identified by the given FQDN.
+ *
+ * @param fqdn The FQDN of the provider to remove
+ * @return true if a provider is removed, false otherwise
+ */
+ public boolean removeProvider(String fqdn) {
+ if (!mProviders.containsKey(fqdn)) {
+ Log.e(TAG, "Config doesn't exist");
+ return false;
+ }
+
+ mProviders.get(fqdn).uninstallCertsAndKeys();
+ mProviders.remove(fqdn);
+ mWifiConfigManager.saveToStore(true /* forceWrite */);
+ Log.d(TAG, "Removed Passpoint configuration: " + fqdn);
+ return true;
+ }
+
+ /**
+ * Return the installed Passpoint provider configurations.
+ *
+ * An empty list will be returned when no provider is installed.
+ *
+ * @return A list of {@link PasspointConfiguration}
+ */
+ public List<PasspointConfiguration> getProviderConfigs() {
+ List<PasspointConfiguration> configs = new ArrayList<>();
+ for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) {
+ configs.add(entry.getValue().getConfig());
+ }
+ return configs;
+ }
+
+ /**
+ * Find the best provider that can provide service through the given AP, which means the
+ * provider contained credential to authenticate with the given AP.
+ *
+ * Here is the current precedence of the matching rule in descending order:
+ * 1. Home Provider
+ * 2. Roaming Provider
+ *
+ * A {code null} will be returned if no matching is found.
+ *
+ * @param scanResult The scan result associated with the AP
+ * @return A pair of {@link PasspointProvider} and match status.
+ */
+ public Pair<PasspointProvider, PasspointMatch> matchProvider(ScanResult scanResult) {
+ // Retrieve the relevant information elements, mainly Roaming Consortium IE and Hotspot 2.0
+ // Vendor Specific IE.
+ InformationElementUtil.RoamingConsortium roamingConsortium =
+ InformationElementUtil.getRoamingConsortiumIE(scanResult.informationElements);
+ InformationElementUtil.Vsa vsa = InformationElementUtil.getHS2VendorSpecificIE(
+ scanResult.informationElements);
+
+ // Lookup ANQP data in the cache.
+ long bssid = Utils.parseMac(scanResult.BSSID);
+ ANQPNetworkKey anqpKey = ANQPNetworkKey.buildKey(scanResult.SSID, bssid, scanResult.hessid,
+ vsa.anqpDomainID);
+ ANQPData anqpEntry = mAnqpCache.getEntry(anqpKey);
+
+ if (anqpEntry == null) {
+ mAnqpRequestManager.requestANQPElements(bssid, anqpKey,
+ roamingConsortium.anqpOICount > 0,
+ vsa.hsRelease == NetworkDetail.HSRelease.R2);
+ Log.d(TAG, "ANQP entry not found for: " + anqpKey);
+ return null;
+ }
+
+ Pair<PasspointProvider, PasspointMatch> bestMatch = null;
+ for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) {
+ PasspointProvider provider = entry.getValue();
+ PasspointMatch matchStatus = provider.match(anqpEntry.getElements());
+ if (matchStatus == PasspointMatch.HomeProvider) {
+ bestMatch = Pair.create(provider, matchStatus);
+ break;
+ }
+ if (matchStatus == PasspointMatch.RoamingProvider && bestMatch == null) {
+ bestMatch = Pair.create(provider, matchStatus);
+ }
+ }
+ if (bestMatch != null) {
+ Log.d(TAG, String.format("Matched %s to %s as %s", scanResult.SSID,
+ bestMatch.first.getConfig().getHomeSp().getFqdn(),
+ bestMatch.second == PasspointMatch.HomeProvider ? "Home Provider"
+ : "Roaming Provider"));
+ } else {
+ Log.d(TAG, "Match not found for " + scanResult.SSID);
+ }
+ return bestMatch;
+ }
+
+ /**
+ * Add a legacy Passpoint configuration represented by a {@link WifiConfiguration} to the
+ * current {@link PasspointManager}.
+ *
+ * This will not trigger a config store write, since this will be invoked as part of the
+ * configuration migration, the caller will be responsible for triggering store write
+ * after the migration is completed.
+ *
+ * @param config {@link WifiConfiguration} representation of the Passpoint configuration
+ * @return true on success
+ */
+ public static boolean addLegacyPasspointConfig(WifiConfiguration config) {
+ if (sPasspointManager == null) {
+ Log.e(TAG, "PasspointManager have not been initialized yet");
+ return false;
+ }
+ Log.d(TAG, "Installing legacy Passpoint configuration: " + config.FQDN);
+ return sPasspointManager.addWifiConfig(config);
+ }
+
+ /**
+ * Sweep the ANQP cache to remove expired entries.
+ */
+ public void sweepCache() {
+ mAnqpCache.sweep();
+ }
+
+ /**
+ * Notify the completion of an ANQP request.
+ * TODO(zqiu): currently the notification is done through WifiMonitor,
+ * will no longer be the case once we switch over to use wificond.
+ */
+ public void notifyANQPDone(AnqpEvent anqpEvent) {
+ mHandler.notifyANQPDone(anqpEvent);
+ }
+
+ /**
+ * Notify the completion of an icon request.
+ * TODO(zqiu): currently the notification is done through WifiMonitor,
+ * will no longer be the case once we switch over to use wificond.
+ */
+ public void notifyIconDone(IconEvent iconEvent) {
+ mHandler.notifyIconDone(iconEvent);
+ }
+
+ /**
+ * Notify the reception of a Wireless Network Management (WNM) frame.
+ * TODO(zqiu): currently the notification is done through WifiMonitor,
+ * will no longer be the case once we switch over to use wificond.
+ */
+ public void receivedWnmFrame(WnmData data) {
+ mHandler.notifyWnmFrameReceived(data);
+ }
+
+ /**
+ * Request the specified icon file |fileName| from the specified AP |bssid|.
+ * @return true if the request is sent successfully, false otherwise
+ */
+ public boolean queryPasspointIcon(long bssid, String fileName) {
+ return mHandler.requestIcon(bssid, fileName);
+ }
+
+ /**
+ * Lookup the ANQP elements associated with the given AP from the cache. An empty map
+ * will be returned if no match found in the cache.
+ *
+ * @param scanResult The scan result associated with the AP
+ * @return Map of ANQP elements
+ */
+ public Map<Constants.ANQPElementType, ANQPElement> getANQPElements(ScanResult scanResult) {
+ // Retrieve the Hotspot 2.0 Vendor Specific IE.
+ InformationElementUtil.Vsa vsa =
+ InformationElementUtil.getHS2VendorSpecificIE(scanResult.informationElements);
+
+ // Lookup ANQP data in the cache.
+ long bssid = Utils.parseMac(scanResult.BSSID);
+ ANQPData anqpEntry = mAnqpCache.getEntry(ANQPNetworkKey.buildKey(
+ scanResult.SSID, bssid, scanResult.hessid, vsa.anqpDomainID));
+ if (anqpEntry != null) {
+ return anqpEntry.getElements();
+ }
+ return new HashMap<Constants.ANQPElementType, ANQPElement>();
+ }
+
+ /**
+ * Match the given WiFi AP to an installed Passpoint provider. A {@link WifiConfiguration}
+ * will be generated and returned if a match is found. The returned {@link WifiConfiguration}
+ * will contained all the necessary credentials for connecting to the given WiFi AP.
+ *
+ * A {code null} will be returned if no matching provider is found.
+ *
+ * @param scanResult The scan result of the given AP
+ * @return {@link WifiConfiguration}
+ */
+ public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
+ if (scanResult == null) {
+ Log.e(TAG, "Attempt to get matching config for a null ScanResult");
+ return null;
+ }
+ Pair<PasspointProvider, PasspointMatch> matchedProvider = matchProvider(scanResult);
+ if (matchedProvider == null) {
+ return null;
+ }
+ WifiConfiguration config = matchedProvider.first.getWifiConfig();
+ config.SSID = ScanResultUtil.createQuotedSSID(scanResult.SSID);
+ if (matchedProvider.second == PasspointMatch.HomeProvider) {
+ config.isHomeProviderNetwork = true;
+ }
+ return config;
+ }
+
+ /**
+ * Dump the current state of PasspointManager to the provided output stream.
+ *
+ * @param pw The output stream to write to
+ */
+ public void dump(PrintWriter pw) {
+ pw.println("Dump of PasspointManager");
+ pw.println("PasspointManager - Providers Begin ---");
+ for (Map.Entry<String, PasspointProvider> entry : mProviders.entrySet()) {
+ pw.println(entry.getValue());
+ }
+ pw.println("PasspointManager - Providers End ---");
+ pw.println("PasspointManager - Next provider ID to be assigned " + mProviderIndex);
+ mAnqpCache.dump(pw);
+ }
+
+ /**
+ * Add a legacy Passpoint configuration represented by a {@link WifiConfiguration}.
+ *
+ * @param wifiConfig {@link WifiConfiguration} representation of the Passpoint configuration
+ * @return true on success
+ */
+ private boolean addWifiConfig(WifiConfiguration wifiConfig) {
+ if (wifiConfig == null) {
+ return false;
+ }
+
+ // Convert to PasspointConfiguration
+ PasspointConfiguration passpointConfig =
+ PasspointProvider.convertFromWifiConfig(wifiConfig);
+ if (passpointConfig == null) {
+ return false;
+ }
+
+ // Setup aliases for enterprise certificates and key.
+ WifiEnterpriseConfig enterpriseConfig = wifiConfig.enterpriseConfig;
+ String caCertificateAliasSuffix = enterpriseConfig.getCaCertificateAlias();
+ String clientCertAndKeyAliasSuffix = enterpriseConfig.getClientCertificateAlias();
+ if (passpointConfig.getCredential().getUserCredential() != null
+ && TextUtils.isEmpty(caCertificateAliasSuffix)) {
+ Log.e(TAG, "Missing CA Certificate for user credential");
+ return false;
+ }
+ if (passpointConfig.getCredential().getCertCredential() != null) {
+ if (TextUtils.isEmpty(caCertificateAliasSuffix)) {
+ Log.e(TAG, "Missing CA certificate for Certificate credential");
+ return false;
+ }
+ if (TextUtils.isEmpty(clientCertAndKeyAliasSuffix)) {
+ Log.e(TAG, "Missing client certificate and key for certificate credential");
+ return false;
+ }
+ }
+
+ // Note that for legacy configuration, the alias for client private key is the same as the
+ // alias for the client certificate.
+ PasspointProvider provider = new PasspointProvider(passpointConfig, mKeyStore,
+ mSimAccessor, mProviderIndex++, wifiConfig.creatorUid,
+ enterpriseConfig.getCaCertificateAlias(),
+ enterpriseConfig.getClientCertificateAlias(),
+ enterpriseConfig.getClientCertificateAlias());
+ mProviders.put(passpointConfig.getHomeSp().getFqdn(), provider);
+ return true;
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java b/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java
deleted file mode 100644
index deb60ed..0000000
--- a/service/java/com/android/server/wifi/hotspot2/PasspointMatchInfo.java
+++ /dev/null
@@ -1,246 +0,0 @@
-package com.android.server.wifi.hotspot2;
-
-import com.android.server.wifi.ScanDetail;
-import com.android.server.wifi.anqp.ANQPElement;
-import com.android.server.wifi.anqp.HSConnectionCapabilityElement;
-import com.android.server.wifi.anqp.HSWanMetricsElement;
-import com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement;
-import com.android.server.wifi.hotspot2.pps.HomeSP;
-
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.Map;
-
-import static com.android.server.wifi.anqp.Constants.ANQPElementType;
-import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv4Availability;
-import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv6Availability;
-
-public class PasspointMatchInfo implements Comparable<PasspointMatchInfo> {
- private final PasspointMatch mPasspointMatch;
- private final ScanDetail mScanDetail;
- private final HomeSP mHomeSP;
- private final int mScore;
-
- private static final Map<IPv4Availability, Integer> sIP4Scores =
- new EnumMap<>(IPv4Availability.class);
- private static final Map<IPv6Availability, Integer> sIP6Scores =
- new EnumMap<>(IPv6Availability.class);
-
- private static final Map<Integer, Map<Integer, Integer>> sPortScores = new HashMap<>();
-
- private static final int IPPROTO_ICMP = 1;
- private static final int IPPROTO_TCP = 6;
- private static final int IPPROTO_UDP = 17;
- private static final int IPPROTO_ESP = 50;
- private static final Map<NetworkDetail.Ant, Integer> sAntScores = new HashMap<>();
-
- static {
- // These are all arbitrarily chosen scores, subject to tuning.
-
- sAntScores.put(NetworkDetail.Ant.FreePublic, 4);
- sAntScores.put(NetworkDetail.Ant.ChargeablePublic, 4);
- sAntScores.put(NetworkDetail.Ant.PrivateWithGuest, 4);
- sAntScores.put(NetworkDetail.Ant.Private, 4);
- sAntScores.put(NetworkDetail.Ant.Personal, 2);
- sAntScores.put(NetworkDetail.Ant.EmergencyOnly, 2);
- sAntScores.put(NetworkDetail.Ant.Wildcard, 1);
- sAntScores.put(NetworkDetail.Ant.TestOrExperimental, 0);
-
- sIP4Scores.put(IPv4Availability.NotAvailable, 0);
- sIP4Scores.put(IPv4Availability.PortRestricted, 1);
- sIP4Scores.put(IPv4Availability.PortRestrictedAndSingleNAT, 1);
- sIP4Scores.put(IPv4Availability.PortRestrictedAndDoubleNAT, 1);
- sIP4Scores.put(IPv4Availability.Unknown, 1);
- sIP4Scores.put(IPv4Availability.Public, 2);
- sIP4Scores.put(IPv4Availability.SingleNAT, 2);
- sIP4Scores.put(IPv4Availability.DoubleNAT, 2);
-
- sIP6Scores.put(IPv6Availability.NotAvailable, 0);
- sIP6Scores.put(IPv6Availability.Reserved, 1);
- sIP6Scores.put(IPv6Availability.Unknown, 1);
- sIP6Scores.put(IPv6Availability.Available, 2);
-
- Map<Integer, Integer> tcpMap = new HashMap<>();
- tcpMap.put(20, 1);
- tcpMap.put(21, 1);
- tcpMap.put(22, 3);
- tcpMap.put(23, 2);
- tcpMap.put(25, 8);
- tcpMap.put(26, 8);
- tcpMap.put(53, 3);
- tcpMap.put(80, 10);
- tcpMap.put(110, 6);
- tcpMap.put(143, 6);
- tcpMap.put(443, 10);
- tcpMap.put(993, 6);
- tcpMap.put(1723, 7);
-
- Map<Integer, Integer> udpMap = new HashMap<>();
- udpMap.put(53, 10);
- udpMap.put(500, 7);
- udpMap.put(5060, 10);
- udpMap.put(4500, 4);
-
- sPortScores.put(IPPROTO_TCP, tcpMap);
- sPortScores.put(IPPROTO_UDP, udpMap);
- }
-
-
- public PasspointMatchInfo(PasspointMatch passpointMatch,
- ScanDetail scanDetail, HomeSP homeSP) {
- mPasspointMatch = passpointMatch;
- mScanDetail = scanDetail;
- mHomeSP = homeSP;
-
- int score;
- if (passpointMatch == PasspointMatch.HomeProvider) {
- score = 100;
- }
- else if (passpointMatch == PasspointMatch.RoamingProvider) {
- score = 0;
- }
- else {
- score = -1000; // Don't expect to see anything not home or roaming.
- }
-
- if (getNetworkDetail().getHSRelease() != null) {
- score += getNetworkDetail().getHSRelease() != NetworkDetail.HSRelease.Unknown ? 50 : 0;
- }
-
- if (getNetworkDetail().hasInterworking()) {
- score += getNetworkDetail().isInternet() ? 20 : -20;
- }
-
- score += (Math.max(200-getNetworkDetail().getStationCount(), 0) *
- (255-getNetworkDetail().getChannelUtilization()) *
- getNetworkDetail().getCapacity()) >>> 26;
- // Gives a value of 23 max capped at 200 stations and max cap 31250
-
- if (getNetworkDetail().hasInterworking()) {
- score += sAntScores.get(getNetworkDetail().getAnt());
- }
-
- Map<ANQPElementType, ANQPElement> anqp = getNetworkDetail().getANQPElements();
-
- if (anqp != null) {
- HSWanMetricsElement wm = (HSWanMetricsElement) anqp.get(ANQPElementType.HSWANMetrics);
-
- if (wm != null) {
- if (wm.getStatus() != HSWanMetricsElement.LinkStatus.Up || wm.isCapped()) {
- score -= 1000;
- } else {
- long scaledSpeed =
- wm.getDlSpeed() * (255 - wm.getDlLoad()) * 8 +
- wm.getUlSpeed() * (255 - wm.getUlLoad()) * 2;
- score += Math.min(scaledSpeed, 255000000L) >>> 23;
- // Max value is 30 capped at 100Mb/s
- }
- }
-
- IPAddressTypeAvailabilityElement ipa =
- (IPAddressTypeAvailabilityElement) anqp.get(ANQPElementType.ANQPIPAddrAvailability);
-
- if (ipa != null) {
- Integer as14 = sIP4Scores.get(ipa.getV4Availability());
- Integer as16 = sIP6Scores.get(ipa.getV6Availability());
- as14 = as14 != null ? as14 : 1;
- as16 = as16 != null ? as16 : 1;
- // Is IPv4 twice as important as IPv6???
- score += as14 * 2 + as16;
- }
-
- HSConnectionCapabilityElement cce =
- (HSConnectionCapabilityElement) anqp.get(ANQPElementType.HSConnCapability);
-
- if (cce != null) {
- score = Math.min(Math.max(protoScore(cce) >> 3, -10), 10);
- }
- }
-
- mScore = score;
- }
-
- public PasspointMatch getPasspointMatch() {
- return mPasspointMatch;
- }
-
- public ScanDetail getScanDetail() {
- return mScanDetail;
- }
-
- public NetworkDetail getNetworkDetail() {
- return mScanDetail.getNetworkDetail();
- }
-
-
- public HomeSP getHomeSP() {
- return mHomeSP;
- }
-
- public int getScore() {
- return mScore;
- }
-
- @Override
- public int compareTo(PasspointMatchInfo that) {
- return getScore() - that.getScore();
- }
-
- private static int protoScore(HSConnectionCapabilityElement cce) {
- int score = 0;
- for (HSConnectionCapabilityElement.ProtocolTuple tuple : cce.getStatusList()) {
- int sign = tuple.getStatus() == HSConnectionCapabilityElement.ProtoStatus.Open ?
- 1 : -1;
-
- int elementScore = 1;
- if (tuple.getProtocol() == IPPROTO_ICMP) {
- elementScore = 1;
- }
- else if (tuple.getProtocol() == IPPROTO_ESP) {
- elementScore = 5;
- }
- else {
- Map<Integer, Integer> protoMap = sPortScores.get(tuple.getProtocol());
- if (protoMap != null) {
- Integer portScore = protoMap.get(tuple.getPort());
- elementScore = portScore != null ? portScore : 0;
- }
- }
- score += elementScore * sign;
- }
- return score;
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (this == thatObject) {
- return true;
- }
- if (thatObject == null || getClass() != thatObject.getClass()) {
- return false;
- }
-
- PasspointMatchInfo that = (PasspointMatchInfo)thatObject;
-
- return getNetworkDetail().equals(that.getNetworkDetail()) &&
- getHomeSP().equals(that.getHomeSP()) &&
- getPasspointMatch().equals(that.getPasspointMatch());
- }
-
- @Override
- public int hashCode() {
- int result = mPasspointMatch != null ? mPasspointMatch.hashCode() : 0;
- result = 31 * result + getNetworkDetail().hashCode();
- result = 31 * result + (mHomeSP != null ? mHomeSP.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "PasspointMatchInfo{" +
- ", mPasspointMatch=" + mPasspointMatch +
- ", mNetworkInfo=" + getNetworkDetail().getSSID() +
- ", mHomeSP=" + mHomeSP.getFQDN() +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
new file mode 100644
index 0000000..132a2f2
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkEvaluator.java
@@ -0,0 +1,189 @@
+/*
+ * 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.hotspot2;
+
+import android.net.wifi.WifiConfiguration;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Pair;
+
+import com.android.server.wifi.NetworkUpdateResult;
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.WifiConfigManager;
+import com.android.server.wifi.WifiNetworkSelector;
+import com.android.server.wifi.util.ScanResultUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is the WifiNetworkSelector.NetworkEvaluator implementation for
+ * Passpoint networks.
+ */
+public class PasspointNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator {
+ private static final String NAME = "PasspointNetworkEvaluator";
+
+ private final PasspointManager mPasspointManager;
+ private final WifiConfigManager mWifiConfigManager;
+ private final LocalLog mLocalLog;
+
+ /**
+ * Contained information for a Passpoint network candidate.
+ */
+ private class PasspointNetworkCandidate {
+ PasspointNetworkCandidate(PasspointProvider provider, PasspointMatch matchStatus,
+ ScanDetail scanDetail) {
+ mProvider = provider;
+ mMatchStatus = matchStatus;
+ mScanDetail = scanDetail;
+ }
+ PasspointProvider mProvider;
+ PasspointMatch mMatchStatus;
+ ScanDetail mScanDetail;
+ }
+
+ public PasspointNetworkEvaluator(PasspointManager passpointManager,
+ WifiConfigManager wifiConfigManager, LocalLog localLog) {
+ mPasspointManager = passpointManager;
+ mWifiConfigManager = wifiConfigManager;
+ mLocalLog = localLog;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public void update(List<ScanDetail> scanDetails) {}
+
+ @Override
+ public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
+ WifiConfiguration currentNetwork, String currentBssid,
+ boolean connected, boolean untrustedNetworkAllowed,
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
+ // Sweep the ANQP cache to remove any expired ANQP entries.
+ mPasspointManager.sweepCache();
+
+ // Go through each ScanDetail and find the best provider for each ScanDetail.
+ List<PasspointNetworkCandidate> candidateList = new ArrayList<>();
+ for (ScanDetail scanDetail : scanDetails) {
+ // Skip non-Passpoint APs.
+ if (!scanDetail.getNetworkDetail().isInterworking()) {
+ continue;
+ }
+
+ // Find the best provider for this ScanDetail.
+ Pair<PasspointProvider, PasspointMatch> bestProvider =
+ mPasspointManager.matchProvider(scanDetail.getScanResult());
+ if (bestProvider != null) {
+ candidateList.add(new PasspointNetworkCandidate(
+ bestProvider.first, bestProvider.second, scanDetail));
+ }
+ }
+
+ // Done if no candidate is found.
+ if (candidateList.isEmpty()) {
+ localLog("No suitable Passpoint network found");
+ return null;
+ }
+
+ // Find the best Passpoint network among all candidates.
+ PasspointNetworkCandidate bestNetwork =
+ findBestNetwork(candidateList, currentNetwork == null ? null : currentNetwork.SSID);
+
+ // Return the configuration for the current connected network if it is the best network.
+ if (currentNetwork != null && TextUtils.equals(currentNetwork.SSID,
+ ScanResultUtil.createQuotedSSID(bestNetwork.mScanDetail.getSSID()))) {
+ localLog("Staying with current Passpoint network " + currentNetwork.SSID);
+ connectableNetworks.add(Pair.create(bestNetwork.mScanDetail, currentNetwork));
+ return currentNetwork;
+ }
+
+ WifiConfiguration config = createWifiConfigForProvider(bestNetwork);
+ connectableNetworks.add(Pair.create(bestNetwork.mScanDetail, config));
+ localLog("Passpoint network to connect to: " + config.SSID);
+ return config;
+ }
+
+ /**
+ * Create and return a WifiConfiguration for the given ScanDetail and PasspointProvider.
+ * The newly created WifiConfiguration will also be added to WifiConfigManager.
+ *
+ * @param networkInfo Contained information for the Passpoint network to connect to
+ * @return {@link WifiConfiguration}
+ */
+ private WifiConfiguration createWifiConfigForProvider(PasspointNetworkCandidate networkInfo) {
+ WifiConfiguration config = networkInfo.mProvider.getWifiConfig();
+ config.SSID = ScanResultUtil.createQuotedSSID(networkInfo.mScanDetail.getSSID());
+ if (networkInfo.mMatchStatus == PasspointMatch.HomeProvider) {
+ config.isHomeProviderNetwork = true;
+ }
+
+ // Add the newly created WifiConfiguration to WifiConfigManager.
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID);
+ if (!result.isSuccess()) {
+ localLog("Failed to add passpoint network");
+ return null;
+ }
+ mWifiConfigManager.enableNetwork(result.getNetworkId(), false, Process.WIFI_UID);
+ mWifiConfigManager.setNetworkCandidateScanResult(result.getNetworkId(),
+ networkInfo.mScanDetail.getScanResult(), 0);
+ mWifiConfigManager.updateScanDetailForNetwork(
+ result.getNetworkId(), networkInfo.mScanDetail);
+ return mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ }
+
+ /**
+ * Given a list of Passpoint networks (with both provider and scan info), find and return
+ * the one with highest score. The score is calculated using
+ * {@link PasspointNetworkScore#calculateScore}.
+ *
+ * @param networkList List of Passpoint networks
+ * @param currentNetworkSsid The SSID of the currently connected network, null if not connected
+ * @return {@link PasspointNetworkCandidate}
+ */
+ private PasspointNetworkCandidate findBestNetwork(
+ List<PasspointNetworkCandidate> networkList, String currentNetworkSsid) {
+ PasspointNetworkCandidate bestCandidate = null;
+ int bestScore = Integer.MIN_VALUE;
+ for (PasspointNetworkCandidate candidate : networkList) {
+ ScanDetail scanDetail = candidate.mScanDetail;
+ PasspointMatch match = candidate.mMatchStatus;
+
+ boolean isActiveNetwork = TextUtils.equals(currentNetworkSsid,
+ ScanResultUtil.createQuotedSSID(scanDetail.getSSID()));
+ int score = PasspointNetworkScore.calculateScore(match == PasspointMatch.HomeProvider,
+ scanDetail, mPasspointManager.getANQPElements(scanDetail.getScanResult()),
+ isActiveNetwork);
+
+ if (score > bestScore) {
+ bestCandidate = candidate;
+ bestScore = score;
+ }
+ }
+ localLog("Best Passpoint network " + bestCandidate.mScanDetail.getSSID() + " provided by "
+ + bestCandidate.mProvider.getConfig().getHomeSp().getFqdn());
+ return bestCandidate;
+ }
+
+ private void localLog(String log) {
+ mLocalLog.log(log);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointNetworkScore.java b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkScore.java
new file mode 100644
index 0000000..03f2e4c
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointNetworkScore.java
@@ -0,0 +1,185 @@
+/*
+ * 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.hotspot2;
+
+import android.net.RssiCurve;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
+import com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement;
+import com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This is an utility class for calculating score for Passpoint networks.
+ */
+public class PasspointNetworkScore {
+ /**
+ * Award points for network that's a Passpoint home provider.
+ */
+ @VisibleForTesting
+ public static final int HOME_PROVIDER_AWARD = 100;
+
+ /**
+ * Award points for network that provides Internet access.
+ */
+ @VisibleForTesting
+ public static final int INTERNET_ACCESS_AWARD = 50;
+
+ /**
+ * Award points for public or private network.
+ */
+ @VisibleForTesting
+ public static final int PUBLIC_OR_PRIVATE_NETWORK_AWARDS = 4;
+
+ /**
+ * Award points for personal or emergency network.
+ */
+ @VisibleForTesting
+ public static final int PERSONAL_OR_EMERGENCY_NETWORK_AWARDS = 2;
+
+ /**
+ * Award points for network providing restricted or unknown IP address.
+ */
+ @VisibleForTesting
+ public static final int RESTRICTED_OR_UNKNOWN_IP_AWARDS = 1;
+
+ /**
+ * Award points for network providing unrestricted IP address.
+ */
+ @VisibleForTesting
+ public static final int UNRESTRICTED_IP_AWARDS = 2;
+
+ /**
+ * Penalty points for network with WAN port that's down or the load already reached the max.
+ */
+ @VisibleForTesting
+ public static final int WAN_PORT_DOWN_OR_CAPPED_PENALTY = 50;
+
+ // Award points for availability of IPv4 and IPv6 addresses.
+ private static final Map<Integer, Integer> IPV4_SCORES = new HashMap<>();
+ private static final Map<Integer, Integer> IPV6_SCORES = new HashMap<>();
+
+ // Award points based on access network type.
+ private static final Map<NetworkDetail.Ant, Integer> NETWORK_TYPE_SCORES = new HashMap<>();
+
+ /**
+ * Curve for calculating score for RSSI level.
+ */
+ @VisibleForTesting
+ public static final RssiCurve RSSI_SCORE = new RssiCurve(-80 /* start */, 20 /* bucketWidth */,
+ new byte[] {-10, 0, 10, 20, 30, 40} /* rssiBuckets */,
+ 20 /* activeNetworkRssiBoost */);
+
+ static {
+ // These are all arbitrarily chosen scores, subject to tuning.
+
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.FreePublic, PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.ChargeablePublic,
+ PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.PrivateWithGuest,
+ PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.Private,
+ PUBLIC_OR_PRIVATE_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.Personal, PERSONAL_OR_EMERGENCY_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.EmergencyOnly,
+ PERSONAL_OR_EMERGENCY_NETWORK_AWARDS);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.Wildcard, 0);
+ NETWORK_TYPE_SCORES.put(NetworkDetail.Ant.TestOrExperimental, 0);
+
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_NOT_AVAILABLE, 0);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED_AND_SINGLE_NAT,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PORT_RESTRICTED_AND_DOUBLE_NAT,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_UNKNOWN,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_PUBLIC, UNRESTRICTED_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_SINGLE_NAT, UNRESTRICTED_IP_AWARDS);
+ IPV4_SCORES.put(IPAddressTypeAvailabilityElement.IPV4_DOUBLE_NAT, UNRESTRICTED_IP_AWARDS);
+
+ IPV6_SCORES.put(IPAddressTypeAvailabilityElement.IPV6_NOT_AVAILABLE, 0);
+ IPV6_SCORES.put(IPAddressTypeAvailabilityElement.IPV6_UNKNOWN,
+ RESTRICTED_OR_UNKNOWN_IP_AWARDS);
+ IPV6_SCORES.put(IPAddressTypeAvailabilityElement.IPV6_AVAILABLE,
+ UNRESTRICTED_IP_AWARDS);
+ }
+
+
+ /**
+ * Calculate and return a score associated with the given Passpoint network.
+ * The score is calculated with the following preferences:
+ * - Prefer home provider
+ * - Prefer network that provides Internet access
+ * - Prefer network with active WAN port with available load
+ * - Prefer network that provides unrestricted IP address
+ * - Prefer currently active network
+ * - Prefer AP with higher RSSI
+ *
+ * This can be expanded for additional preference in the future (e.g. AP station count, link
+ * speed, and etc).
+ *
+ * @param isHomeProvider Flag indicating home provider
+ * @param scanDetail The ScanDetail associated with the AP
+ * @param isActiveNetwork Flag indicating current active network
+ * @return integer score
+ */
+ public static int calculateScore(boolean isHomeProvider, ScanDetail scanDetail,
+ Map<ANQPElementType, ANQPElement> anqpElements, boolean isActiveNetwork) {
+ NetworkDetail networkDetail = scanDetail.getNetworkDetail();
+ int score = 0;
+ if (isHomeProvider) {
+ score += HOME_PROVIDER_AWARD;
+ }
+
+ // Adjust score based on Internet accessibility.
+ score += (networkDetail.isInternet() ? 1 : -1) * INTERNET_ACCESS_AWARD;
+
+ // Adjust score based on the network type.
+ score += NETWORK_TYPE_SCORES.get(networkDetail.getAnt());
+
+ if (anqpElements != null) {
+ HSWanMetricsElement wm =
+ (HSWanMetricsElement) anqpElements.get(ANQPElementType.HSWANMetrics);
+ if (wm != null) {
+ if (wm.getStatus() != HSWanMetricsElement.LINK_STATUS_UP || wm.isCapped()) {
+ score -= WAN_PORT_DOWN_OR_CAPPED_PENALTY;
+ }
+ }
+
+ IPAddressTypeAvailabilityElement ipa = (IPAddressTypeAvailabilityElement)
+ anqpElements.get(ANQPElementType.ANQPIPAddrAvailability);
+
+ if (ipa != null) {
+ Integer v4Score = IPV4_SCORES.get(ipa.getV4Availability());
+ Integer v6Score = IPV6_SCORES.get(ipa.getV6Availability());
+ v4Score = v4Score != null ? v4Score : 0;
+ v6Score = v6Score != null ? v6Score : 0;
+ score += (v4Score + v6Score);
+ }
+ }
+
+ score += RSSI_SCORE.lookupScore(scanDetail.getScanResult().level, isActiveNetwork);
+ return score;
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
new file mode 100644
index 0000000..c41c49a
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
@@ -0,0 +1,98 @@
+/*
+ * 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.hotspot2;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.SIMAccessor;
+import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.WifiNative;
+
+/**
+ * Factory class for creating Passpoint related objects. Useful for mocking object creations
+ * in the unit tests.
+ */
+public class PasspointObjectFactory{
+ /**
+ * Create a PasspointEventHandler instance.
+ *
+ * @param wifiNative Instance of {@link WifiNative}
+ * @param callbacks Instance of {@link PasspointEventHandler.Callbacks}
+ * @return {@link PasspointEventHandler}
+ */
+ public PasspointEventHandler makePasspointEventHandler(WifiNative wifiNative,
+ PasspointEventHandler.Callbacks callbacks) {
+ return new PasspointEventHandler(wifiNative, callbacks);
+ }
+
+ /**
+ * Create a PasspointProvider instance.
+ *
+ * @param keyStore Instance of {@link WifiKeyStore}
+ * @param config Configuration for the provider
+ * @param providerId Unique identifier for the provider
+ * @return {@link PasspointProvider}
+ */
+ public PasspointProvider makePasspointProvider(PasspointConfiguration config,
+ WifiKeyStore keyStore, SIMAccessor simAccessor, long providerId, int creatorUid) {
+ return new PasspointProvider(config, keyStore, simAccessor, providerId, creatorUid);
+ }
+
+ /**
+ * Create a {@link PasspointConfigStoreData} instance.
+ *
+ * @param keyStore Instance of {@link WifiKeyStore}
+ * @param simAccessor Instance of {@link SIMAccessor}
+ * @param dataSource Passpoint configuration data source
+ * @return {@link PasspointConfigStoreData}
+ */
+ public PasspointConfigStoreData makePasspointConfigStoreData(WifiKeyStore keyStore,
+ SIMAccessor simAccessor, PasspointConfigStoreData.DataSource dataSource) {
+ return new PasspointConfigStoreData(keyStore, simAccessor, dataSource);
+ }
+
+ /**
+ * Create a AnqpCache instance.
+ *
+ * @param clock Instance of {@link Clock}
+ * @return {@link AnqpCache}
+ */
+ public AnqpCache makeAnqpCache(Clock clock) {
+ return new AnqpCache(clock);
+ }
+
+ /**
+ * Create an instance of {@link ANQPRequestManager}.
+ *
+ * @param handler Instance of {@link PasspointEventHandler}
+ * @param clock Instance of {@link Clock}
+ * @return {@link ANQPRequestManager}
+ */
+ public ANQPRequestManager makeANQPRequestManager(PasspointEventHandler handler, Clock clock) {
+ return new ANQPRequestManager(handler, clock);
+ }
+
+ /**
+ * Create an instance of {@link CertificateVerifier}.
+ *
+ * @return {@link CertificateVerifier}
+ */
+ public CertificateVerifier makeCertificateVerifier() {
+ return new CertificateVerifier();
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
new file mode 100644
index 0000000..33867bb
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
@@ -0,0 +1,620 @@
+/*
+ * 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.hotspot2;
+
+import android.net.wifi.EAPConstants;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.Credential.SimCredential;
+import android.net.wifi.hotspot2.pps.Credential.UserCredential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.security.Credentials;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.SIMAccessor;
+import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
+import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmElement;
+import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement;
+import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
+import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
+import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Abstraction for Passpoint service provider. This class contains the both static
+ * Passpoint configuration data and the runtime data (e.g. blacklisted SSIDs, statistics).
+ */
+public class PasspointProvider {
+ private static final String TAG = "PasspointProvider";
+
+ /**
+ * Used as part of alias string for certificates and keys. The alias string is in the format
+ * of: [KEY_TYPE]_HS2_[ProviderID]
+ * For example: "CACERT_HS2_0", "USRCERT_HS2_0", "USRPKEY_HS2_0"
+ */
+ private static final String ALIAS_HS_TYPE = "HS2_";
+
+ private final PasspointConfiguration mConfig;
+ private final WifiKeyStore mKeyStore;
+
+ /**
+ * Aliases for the private keys and certificates installed in the keystore. Each alias
+ * is a suffix of the actual certificate or key name installed in the keystore. The
+ * certificate or key name in the keystore is consist of |Type|_|alias|.
+ * This will be consistent with the usage of the term "alias" in {@link WifiEnterpriseConfig}.
+ */
+ private String mCaCertificateAlias;
+ private String mClientPrivateKeyAlias;
+ private String mClientCertificateAlias;
+
+ private final long mProviderId;
+ private final int mCreatorUid;
+
+ private final IMSIParameter mImsiParameter;
+ private final List<String> mMatchingSIMImsiList;
+
+ private final int mEAPMethodID;
+ private final AuthParam mAuthParam;
+
+ public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore,
+ SIMAccessor simAccessor, long providerId, int creatorUid) {
+ this(config, keyStore, simAccessor, providerId, creatorUid, null, null, null);
+ }
+
+ public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore,
+ SIMAccessor simAccessor, long providerId, int creatorUid, String caCertificateAlias,
+ String clientCertificateAlias, String clientPrivateKeyAlias) {
+ // Maintain a copy of the configuration to avoid it being updated by others.
+ mConfig = new PasspointConfiguration(config);
+ mKeyStore = keyStore;
+ mProviderId = providerId;
+ mCreatorUid = creatorUid;
+ mCaCertificateAlias = caCertificateAlias;
+ mClientCertificateAlias = clientCertificateAlias;
+ mClientPrivateKeyAlias = clientPrivateKeyAlias;
+
+ // Setup EAP method and authentication parameter based on the credential.
+ if (mConfig.getCredential().getUserCredential() != null) {
+ mEAPMethodID = EAPConstants.EAP_TTLS;
+ mAuthParam = new NonEAPInnerAuth(NonEAPInnerAuth.getAuthTypeID(
+ mConfig.getCredential().getUserCredential().getNonEapInnerMethod()));
+ mImsiParameter = null;
+ mMatchingSIMImsiList = null;
+ } else if (mConfig.getCredential().getCertCredential() != null) {
+ mEAPMethodID = EAPConstants.EAP_TLS;
+ mAuthParam = null;
+ mImsiParameter = null;
+ mMatchingSIMImsiList = null;
+ } else {
+ mEAPMethodID = mConfig.getCredential().getSimCredential().getEapType();
+ mAuthParam = null;
+ mImsiParameter = IMSIParameter.build(
+ mConfig.getCredential().getSimCredential().getImsi());
+ mMatchingSIMImsiList = simAccessor.getMatchingImsis(mImsiParameter);
+ }
+ }
+
+ public PasspointConfiguration getConfig() {
+ // Return a copy of the configuration to avoid it being updated by others.
+ return new PasspointConfiguration(mConfig);
+ }
+
+ public String getCaCertificateAlias() {
+ return mCaCertificateAlias;
+ }
+
+ public String getClientPrivateKeyAlias() {
+ return mClientPrivateKeyAlias;
+ }
+
+ public String getClientCertificateAlias() {
+ return mClientCertificateAlias;
+ }
+
+ public long getProviderId() {
+ return mProviderId;
+ }
+
+ public int getCreatorUid() {
+ return mCreatorUid;
+ }
+
+ /**
+ * Install certificates and key based on current configuration.
+ * Note: the certificates and keys in the configuration will get cleared once
+ * they're installed in the keystore.
+ *
+ * @return true on success
+ */
+ public boolean installCertsAndKeys() {
+ // Install CA certificate.
+ if (mConfig.getCredential().getCaCertificate() != null) {
+ String certName = Credentials.CA_CERTIFICATE + ALIAS_HS_TYPE + mProviderId;
+ if (!mKeyStore.putCertInKeyStore(certName,
+ mConfig.getCredential().getCaCertificate())) {
+ Log.e(TAG, "Failed to install CA Certificate");
+ uninstallCertsAndKeys();
+ return false;
+ }
+ mCaCertificateAlias = ALIAS_HS_TYPE + mProviderId;
+ }
+
+ // Install the client private key.
+ if (mConfig.getCredential().getClientPrivateKey() != null) {
+ String keyName = Credentials.USER_PRIVATE_KEY + ALIAS_HS_TYPE + mProviderId;
+ if (!mKeyStore.putKeyInKeyStore(keyName,
+ mConfig.getCredential().getClientPrivateKey())) {
+ Log.e(TAG, "Failed to install client private key");
+ uninstallCertsAndKeys();
+ return false;
+ }
+ mClientPrivateKeyAlias = ALIAS_HS_TYPE + mProviderId;
+ }
+
+ // Install the client certificate.
+ if (mConfig.getCredential().getClientCertificateChain() != null) {
+ X509Certificate clientCert = getClientCertificate(
+ mConfig.getCredential().getClientCertificateChain(),
+ mConfig.getCredential().getCertCredential().getCertSha256Fingerprint());
+ if (clientCert == null) {
+ Log.e(TAG, "Failed to locate client certificate");
+ uninstallCertsAndKeys();
+ return false;
+ }
+ String certName = Credentials.USER_CERTIFICATE + ALIAS_HS_TYPE + mProviderId;
+ if (!mKeyStore.putCertInKeyStore(certName, clientCert)) {
+ Log.e(TAG, "Failed to install client certificate");
+ uninstallCertsAndKeys();
+ return false;
+ }
+ mClientCertificateAlias = ALIAS_HS_TYPE + mProviderId;
+ }
+
+ // Clear the keys and certificates in the configuration.
+ mConfig.getCredential().setCaCertificate(null);
+ mConfig.getCredential().setClientPrivateKey(null);
+ mConfig.getCredential().setClientCertificateChain(null);
+ return true;
+ }
+
+ /**
+ * Remove any installed certificates and key.
+ */
+ public void uninstallCertsAndKeys() {
+ if (mCaCertificateAlias != null) {
+ if (!mKeyStore.removeEntryFromKeyStore(
+ Credentials.CA_CERTIFICATE + mCaCertificateAlias)) {
+ Log.e(TAG, "Failed to remove entry: " + mCaCertificateAlias);
+ }
+ mCaCertificateAlias = null;
+ }
+ if (mClientPrivateKeyAlias != null) {
+ if (!mKeyStore.removeEntryFromKeyStore(
+ Credentials.USER_PRIVATE_KEY + mClientPrivateKeyAlias)) {
+ Log.e(TAG, "Failed to remove entry: " + mClientPrivateKeyAlias);
+ }
+ mClientPrivateKeyAlias = null;
+ }
+ if (mClientCertificateAlias != null) {
+ if (!mKeyStore.removeEntryFromKeyStore(
+ Credentials.USER_CERTIFICATE + mClientCertificateAlias)) {
+ Log.e(TAG, "Failed to remove entry: " + mClientCertificateAlias);
+ }
+ mClientCertificateAlias = null;
+ }
+ }
+
+ /**
+ * Return the matching status with the given AP, based on the ANQP elements from the AP.
+ *
+ * @param anqpElements ANQP elements from the AP
+ * @return {@link PasspointMatch}
+ */
+ public PasspointMatch match(Map<ANQPElementType, ANQPElement> anqpElements) {
+ PasspointMatch providerMatch = matchProvider(anqpElements);
+
+ // Perform authentication match against the NAI Realm.
+ int authMatch = ANQPMatcher.matchNAIRealm(
+ (NAIRealmElement) anqpElements.get(ANQPElementType.ANQPNAIRealm),
+ mConfig.getCredential().getRealm(), mEAPMethodID, mAuthParam);
+
+ // Auth mismatch, demote provider match.
+ if (authMatch == AuthMatch.NONE) {
+ return PasspointMatch.None;
+ }
+
+ // No realm match, return provider match as is.
+ if ((authMatch & AuthMatch.REALM) == 0) {
+ return providerMatch;
+ }
+
+ // Realm match, promote provider match to roaming if no other provider match is found.
+ return providerMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider
+ : providerMatch;
+ }
+
+ /**
+ * Generate a WifiConfiguration based on the provider's configuration. The generated
+ * WifiConfiguration will include all the necessary credentials for network connection except
+ * the SSID, which should be added by the caller when the config is being used for network
+ * connection.
+ *
+ * @return {@link WifiConfiguration}
+ */
+ public WifiConfiguration getWifiConfig() {
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = mConfig.getHomeSp().getFqdn();
+ if (mConfig.getHomeSp().getRoamingConsortiumOis() != null) {
+ wifiConfig.roamingConsortiumIds = Arrays.copyOf(
+ mConfig.getHomeSp().getRoamingConsortiumOis(),
+ mConfig.getHomeSp().getRoamingConsortiumOis().length);
+ }
+ wifiConfig.providerFriendlyName = mConfig.getHomeSp().getFriendlyName();
+ wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setRealm(mConfig.getCredential().getRealm());
+ enterpriseConfig.setDomainSuffixMatch(mConfig.getHomeSp().getFqdn());
+ if (mConfig.getCredential().getUserCredential() != null) {
+ buildEnterpriseConfigForUserCredential(enterpriseConfig,
+ mConfig.getCredential().getUserCredential());
+ setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm());
+ } else if (mConfig.getCredential().getCertCredential() != null) {
+ buildEnterpriseConfigForCertCredential(enterpriseConfig);
+ setAnonymousIdentityToNaiRealm(enterpriseConfig, mConfig.getCredential().getRealm());
+ } else {
+ buildEnterpriseConfigForSimCredential(enterpriseConfig,
+ mConfig.getCredential().getSimCredential());
+ }
+ wifiConfig.enterpriseConfig = enterpriseConfig;
+ return wifiConfig;
+ }
+
+ /**
+ * Convert a legacy {@link WifiConfiguration} representation of a Passpoint configuration to
+ * a {@link PasspointConfiguration}. This is used for migrating legacy Passpoint
+ * configuration (release N and older).
+ *
+ * @param wifiConfig The {@link WifiConfiguration} to convert
+ * @return {@link PasspointConfiguration}
+ */
+ public static PasspointConfiguration convertFromWifiConfig(WifiConfiguration wifiConfig) {
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+
+ // Setup HomeSP.
+ HomeSp homeSp = new HomeSp();
+ if (TextUtils.isEmpty(wifiConfig.FQDN)) {
+ Log.e(TAG, "Missing FQDN");
+ return null;
+ }
+ homeSp.setFqdn(wifiConfig.FQDN);
+ homeSp.setFriendlyName(wifiConfig.providerFriendlyName);
+ if (wifiConfig.roamingConsortiumIds != null) {
+ homeSp.setRoamingConsortiumOis(Arrays.copyOf(
+ wifiConfig.roamingConsortiumIds, wifiConfig.roamingConsortiumIds.length));
+ }
+ passpointConfig.setHomeSp(homeSp);
+
+ // Setup Credential.
+ Credential credential = new Credential();
+ credential.setRealm(wifiConfig.enterpriseConfig.getRealm());
+ switch (wifiConfig.enterpriseConfig.getEapMethod()) {
+ case WifiEnterpriseConfig.Eap.TTLS:
+ credential.setUserCredential(buildUserCredentialFromEnterpriseConfig(
+ wifiConfig.enterpriseConfig));
+ break;
+ case WifiEnterpriseConfig.Eap.TLS:
+ Credential.CertificateCredential certCred = new Credential.CertificateCredential();
+ certCred.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3);
+ credential.setCertCredential(certCred);
+ break;
+ case WifiEnterpriseConfig.Eap.SIM:
+ credential.setSimCredential(buildSimCredentialFromEnterpriseConfig(
+ EAPConstants.EAP_SIM, wifiConfig.enterpriseConfig));
+ break;
+ case WifiEnterpriseConfig.Eap.AKA:
+ credential.setSimCredential(buildSimCredentialFromEnterpriseConfig(
+ EAPConstants.EAP_AKA, wifiConfig.enterpriseConfig));
+ break;
+ case WifiEnterpriseConfig.Eap.AKA_PRIME:
+ credential.setSimCredential(buildSimCredentialFromEnterpriseConfig(
+ EAPConstants.EAP_AKA_PRIME, wifiConfig.enterpriseConfig));
+ break;
+ default:
+ Log.e(TAG, "Unsupport EAP method: " + wifiConfig.enterpriseConfig.getEapMethod());
+ return null;
+ }
+ if (credential.getUserCredential() == null && credential.getCertCredential() == null
+ && credential.getSimCredential() == null) {
+ Log.e(TAG, "Missing credential");
+ return null;
+ }
+ passpointConfig.setCredential(credential);
+
+ return passpointConfig;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof PasspointProvider)) {
+ return false;
+ }
+ PasspointProvider that = (PasspointProvider) thatObject;
+ return mProviderId == that.mProviderId
+ && TextUtils.equals(mCaCertificateAlias, that.mCaCertificateAlias)
+ && TextUtils.equals(mClientCertificateAlias, that.mClientCertificateAlias)
+ && TextUtils.equals(mClientPrivateKeyAlias, that.mClientPrivateKeyAlias)
+ && (mConfig == null ? that.mConfig == null : mConfig.equals(that.mConfig));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mProviderId, mCaCertificateAlias, mClientCertificateAlias,
+ mClientPrivateKeyAlias, mConfig);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("ProviderId: ").append(mProviderId).append("\n");
+ builder.append("CreatorUID: ").append(mCreatorUid).append("\n");
+ builder.append("Configuration Begin ---\n");
+ builder.append(mConfig);
+ builder.append("Configuration End ---\n");
+ return builder.toString();
+ }
+
+ /**
+ * Retrieve the client certificate from the certificates chain. The certificate
+ * with the matching SHA256 digest is the client certificate.
+ *
+ * @param certChain The client certificates chain
+ * @param expectedSha256Fingerprint The expected SHA256 digest of the client certificate
+ * @return {@link java.security.cert.X509Certificate}
+ */
+ private static X509Certificate getClientCertificate(X509Certificate[] certChain,
+ byte[] expectedSha256Fingerprint) {
+ if (certChain == null) {
+ return null;
+ }
+ try {
+ MessageDigest digester = MessageDigest.getInstance("SHA-256");
+ for (X509Certificate certificate : certChain) {
+ digester.reset();
+ byte[] fingerprint = digester.digest(certificate.getEncoded());
+ if (Arrays.equals(expectedSha256Fingerprint, fingerprint)) {
+ return certificate;
+ }
+ }
+ } catch (CertificateEncodingException | NoSuchAlgorithmException e) {
+ return null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Perform a provider match based on the given ANQP elements.
+ *
+ * @param anqpElements List of ANQP elements
+ * @return {@link PasspointMatch}
+ */
+ private PasspointMatch matchProvider(Map<ANQPElementType, ANQPElement> anqpElements) {
+ // Domain name matching.
+ if (ANQPMatcher.matchDomainName(
+ (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName),
+ mConfig.getHomeSp().getFqdn(), mImsiParameter, mMatchingSIMImsiList)) {
+ return PasspointMatch.HomeProvider;
+ }
+
+ // Roaming Consortium OI matching.
+ if (ANQPMatcher.matchRoamingConsortium(
+ (RoamingConsortiumElement) anqpElements.get(ANQPElementType.ANQPRoamingConsortium),
+ mConfig.getHomeSp().getRoamingConsortiumOis())) {
+ return PasspointMatch.RoamingProvider;
+ }
+
+ // 3GPP Network matching.
+ if (ANQPMatcher.matchThreeGPPNetwork(
+ (ThreeGPPNetworkElement) anqpElements.get(ANQPElementType.ANQP3GPPNetwork),
+ mImsiParameter, mMatchingSIMImsiList)) {
+ return PasspointMatch.RoamingProvider;
+ }
+ return PasspointMatch.None;
+ }
+
+ /**
+ * Fill in WifiEnterpriseConfig with information from an user credential.
+ *
+ * @param config Instance of {@link WifiEnterpriseConfig}
+ * @param credential Instance of {@link UserCredential}
+ */
+ private void buildEnterpriseConfigForUserCredential(WifiEnterpriseConfig config,
+ Credential.UserCredential credential) {
+ byte[] pwOctets = Base64.decode(credential.getPassword(), Base64.DEFAULT);
+ String decodedPassword = new String(pwOctets, StandardCharsets.UTF_8);
+ config.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
+ config.setIdentity(credential.getUsername());
+ config.setPassword(decodedPassword);
+ config.setCaCertificateAlias(mCaCertificateAlias);
+ int phase2Method = WifiEnterpriseConfig.Phase2.NONE;
+ switch (credential.getNonEapInnerMethod()) {
+ case Credential.UserCredential.AUTH_METHOD_PAP:
+ phase2Method = WifiEnterpriseConfig.Phase2.PAP;
+ break;
+ case Credential.UserCredential.AUTH_METHOD_MSCHAP:
+ phase2Method = WifiEnterpriseConfig.Phase2.MSCHAP;
+ break;
+ case Credential.UserCredential.AUTH_METHOD_MSCHAPV2:
+ phase2Method = WifiEnterpriseConfig.Phase2.MSCHAPV2;
+ break;
+ default:
+ // Should never happen since this is already validated when the provider is
+ // added.
+ Log.wtf(TAG, "Unsupported Auth: " + credential.getNonEapInnerMethod());
+ break;
+ }
+ config.setPhase2Method(phase2Method);
+ }
+
+ /**
+ * Fill in WifiEnterpriseConfig with information from a certificate credential.
+ *
+ * @param config Instance of {@link WifiEnterpriseConfig}
+ */
+ private void buildEnterpriseConfigForCertCredential(WifiEnterpriseConfig config) {
+ config.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ config.setClientCertificateAlias(mClientCertificateAlias);
+ config.setCaCertificateAlias(mCaCertificateAlias);
+ }
+
+ /**
+ * Fill in WifiEnterpriseConfig with information from a SIM credential.
+ *
+ * @param config Instance of {@link WifiEnterpriseConfig}
+ * @param credential Instance of {@link SimCredential}
+ */
+ private void buildEnterpriseConfigForSimCredential(WifiEnterpriseConfig config,
+ Credential.SimCredential credential) {
+ int eapMethod = WifiEnterpriseConfig.Eap.NONE;
+ switch(credential.getEapType()) {
+ case EAPConstants.EAP_SIM:
+ eapMethod = WifiEnterpriseConfig.Eap.SIM;
+ break;
+ case EAPConstants.EAP_AKA:
+ eapMethod = WifiEnterpriseConfig.Eap.AKA;
+ break;
+ case EAPConstants.EAP_AKA_PRIME:
+ eapMethod = WifiEnterpriseConfig.Eap.AKA_PRIME;
+ break;
+ default:
+ // Should never happen since this is already validated when the provider is
+ // added.
+ Log.wtf(TAG, "Unsupported EAP Method: " + credential.getEapType());
+ break;
+ }
+ config.setEapMethod(eapMethod);
+ config.setPlmn(credential.getImsi());
+ }
+
+ private static void setAnonymousIdentityToNaiRealm(WifiEnterpriseConfig config, String realm) {
+ /**
+ * Set WPA supplicant's anonymous identity field to a string containing the NAI realm, so
+ * that this value will be sent to the EAP server as part of the EAP-Response/ Identity
+ * packet. WPA supplicant will reset this field after using it for the EAP-Response/Identity
+ * packet, and revert to using the (real) identity field for subsequent transactions that
+ * request an identity (e.g. in EAP-TTLS).
+ *
+ * This NAI realm value (the portion of the identity after the '@') is used to tell the
+ * AAA server which AAA/H to forward packets to. The hardcoded username, "anonymous", is a
+ * placeholder that is not used--it is set to this value by convention. See Section 5.1 of
+ * RFC3748 for more details.
+ *
+ * NOTE: we do not set this value for EAP-SIM/AKA/AKA', since the EAP server expects the
+ * EAP-Response/Identity packet to contain an actual, IMSI-based identity, in order to
+ * identify the device.
+ */
+ config.setAnonymousIdentity("anonymous@" + realm);
+ }
+
+ /**
+ * Helper function for creating a
+ * {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} from the given
+ * {@link WifiEnterpriseConfig}
+ *
+ * @param config The enterprise configuration containing the credential
+ * @return {@link android.net.wifi.hotspot2.pps.Credential.UserCredential}
+ */
+ private static Credential.UserCredential buildUserCredentialFromEnterpriseConfig(
+ WifiEnterpriseConfig config) {
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setEapType(EAPConstants.EAP_TTLS);
+
+ if (TextUtils.isEmpty(config.getIdentity())) {
+ Log.e(TAG, "Missing username for user credential");
+ return null;
+ }
+ userCredential.setUsername(config.getIdentity());
+
+ if (TextUtils.isEmpty(config.getPassword())) {
+ Log.e(TAG, "Missing password for user credential");
+ return null;
+ }
+ String encodedPassword =
+ new String(Base64.encode(config.getPassword().getBytes(StandardCharsets.UTF_8),
+ Base64.DEFAULT), StandardCharsets.UTF_8);
+ userCredential.setPassword(encodedPassword);
+
+ switch(config.getPhase2Method()) {
+ case WifiEnterpriseConfig.Phase2.PAP:
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_PAP);
+ break;
+ case WifiEnterpriseConfig.Phase2.MSCHAP:
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAP);
+ break;
+ case WifiEnterpriseConfig.Phase2.MSCHAPV2:
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2);
+ break;
+ default:
+ Log.e(TAG, "Unsupported phase2 method for TTLS: " + config.getPhase2Method());
+ return null;
+ }
+ return userCredential;
+ }
+
+ /**
+ * Helper function for creating a
+ * {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} from the given
+ * {@link WifiEnterpriseConfig}
+ *
+ * @param eapType The EAP type of the SIM credential
+ * @param config The enterprise configuration containing the credential
+ * @return {@link android.net.wifi.hotspot2.pps.Credential.SimCredential}
+ */
+ private static Credential.SimCredential buildSimCredentialFromEnterpriseConfig(
+ int eapType, WifiEnterpriseConfig config) {
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ if (TextUtils.isEmpty(config.getPlmn())) {
+ Log.e(TAG, "Missing IMSI for SIM credential");
+ return null;
+ }
+ simCredential.setImsi(config.getPlmn());
+ simCredential.setEapType(eapType);
+ return simCredential;
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointXmlUtils.java b/service/java/com/android/server/wifi/hotspot2/PasspointXmlUtils.java
new file mode 100644
index 0000000..de2b9c0
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointXmlUtils.java
@@ -0,0 +1,890 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.net.wifi.hotspot2.pps.Policy;
+import android.net.wifi.hotspot2.pps.UpdateParameter;
+
+import com.android.internal.util.XmlUtils;
+import com.android.server.wifi.util.XmlUtil;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class for serialize and deserialize Passpoint related configurations to/from XML string.
+ */
+public class PasspointXmlUtils {
+ // XML section header tags.
+ private static final String XML_TAG_SECTION_HEADER_HOMESP = "HomeSP";
+ private static final String XML_TAG_SECTION_HEADER_CREDENTIAL = "Credential";
+ private static final String XML_TAG_SECTION_HEADER_USER_CREDENTIAL = "UserCredential";
+ private static final String XML_TAG_SECTION_HEADER_CERT_CREDENTIAL = "CertCredential";
+ private static final String XML_TAG_SECTION_HEADER_SIM_CREDENTIAL = "SimCredential";
+ private static final String XML_TAG_SECTION_HEADER_POLICY = "Policy";
+ private static final String XML_TAG_SECTION_HEADER_PREFERRED_ROAMING_PARTNER_LIST =
+ "RoamingPartnerList";
+ private static final String XML_TAG_SECTION_HEADER_ROAMING_PARTNER = "RoamingPartner";
+ private static final String XML_TAG_SECTION_HEADER_POLICY_UPDATE = "PolicyUpdate";
+ private static final String XML_TAG_SECTION_HEADER_SUBSCRIPTION_UPDATE = "SubscriptionUpdate";
+ private static final String XML_TAG_SECTION_HEADER_REQUIRED_PROTO_PORT_MAP =
+ "RequiredProtoPortMap";
+ private static final String XML_TAG_SECTION_HEADER_PROTO_PORT = "ProtoPort";
+
+ // XML value tags.
+ private static final String XML_TAG_FQDN = "FQDN";
+ private static final String XML_TAG_FRIENDLY_NAME = "FriendlyName";
+ private static final String XML_TAG_ICON_URL = "IconURL";
+ private static final String XML_TAG_HOME_NETWORK_IDS = "HomeNetworkIDs";
+ private static final String XML_TAG_MATCH_ALL_OIS = "MatchAllOIs";
+ private static final String XML_TAG_MATCH_ANY_OIS = "MatchAnyOIs";
+ private static final String XML_TAG_OTHER_HOME_PARTNERS = "OtherHomePartners";
+ private static final String XML_TAG_ROAMING_CONSORTIUM_OIS = "RoamingConsortiumOIs";
+ private static final String XML_TAG_CREATION_TIME = "CreationTime";
+ private static final String XML_TAG_EXPIRATION_TIME = "ExpirationTime";
+ private static final String XML_TAG_REALM = "Realm";
+ private static final String XML_TAG_CHECK_AAA_SERVER_CERT_STATUS = "CheckAAAServerCertStatus";
+ private static final String XML_TAG_USERNAME = "Username";
+ private static final String XML_TAG_PASSWORD = "Password";
+ private static final String XML_TAG_MACHINE_MANAGED = "MachineManaged";
+ private static final String XML_TAG_SOFT_TOKEN_APP = "SoftTokenApp";
+ private static final String XML_TAG_ABLE_TO_SHARE = "AbleToShare";
+ private static final String XML_TAG_EAP_TYPE = "EAPType";
+ private static final String XML_TAG_NON_EAP_INNER_METHOD = "NonEAPInnerMethod";
+ private static final String XML_TAG_CERT_TYPE = "CertType";
+ private static final String XML_TAG_CERT_SHA256_FINGERPRINT = "CertSHA256Fingerprint";
+ private static final String XML_TAG_IMSI = "IMSI";
+ private static final String XML_TAG_MIN_HOME_DOWNLINK_BANDWIDTH = "MinHomeDownlinkBandwidth";
+ private static final String XML_TAG_MIN_HOME_UPLINK_BANDWIDTH = "MinHomeUplinkBandwidth";
+ private static final String XML_TAG_MIN_ROAMING_DOWNLINK_BANDWIDTH =
+ "MinRoamingDownlinkBandwidth";
+ private static final String XML_TAG_MIN_ROAMING_UPLINK_BANDWIDTH =
+ "MinRoamingUplinkBandwidth";
+ private static final String XML_TAG_EXCLUDED_SSID_LIST = "ExcludedSSIDList";
+ private static final String XML_TAG_PROTO = "Proto";
+ private static final String XML_TAG_PORTS = "Ports";
+ private static final String XML_TAG_MAXIMUM_BSS_LOAD_VALUE = "MaximumBSSLoadValue";
+ private static final String XML_TAG_FQDN_EXACT_MATCH = "FQDNExactMatch";
+ private static final String XML_TAG_PRIORITY = "Priority";
+ private static final String XML_TAG_COUNTRIES = "Countries";
+ private static final String XML_TAG_UPDATE_INTERVAL = "UpdateInterval";
+ private static final String XML_TAG_UPDATE_METHOD = "UpdateMethod";
+ private static final String XML_TAG_RESTRICTION = "Restriction";
+ private static final String XML_TAG_SERVER_URI = "ServerURI";
+ private static final String XML_TAG_TRUST_ROOT_CERT_URL = "TrustRootCertURL";
+ private static final String XML_TAG_TRUST_ROOT_CERT_SHA256_FINGERPRINT =
+ "TrustRootCertSHA256Fingerprint";
+ private static final String XML_TAG_TRUST_ROOT_CERT_LIST = "TrustRootCertList";
+ private static final String XML_TAG_UPDATE_IDENTIFIER = "UpdateIdentifier";
+ private static final String XML_TAG_CREDENTIAL_PRIORITY = "CredentialPriority";
+ private static final String XML_TAG_SUBSCRIPTION_CREATION_TIME = "SubscriptionCreationTime";
+ private static final String XML_TAG_SUBSCRIPTION_EXPIRATION_TIME =
+ "SubscriptionExpirationTime";
+ private static final String XML_TAG_SUBSCRIPTION_TYPE = "SubscriptionType";
+ private static final String XML_TAG_USAGE_LIMIT_TIME_PERIOD = "UsageLimitTimePeriod";
+ private static final String XML_TAG_USAGE_LIMIT_START_TIME = "UsageLimitStartTime";
+ private static final String XML_TAG_USAGE_LIMIT_DATA_LIMIT = "UsageLimitDataLimit";
+ private static final String XML_TAG_USAGE_LIMIT_TIME_LIMIT = "UsageLimitTimeLimit";
+
+ /**
+ * Serialize a {@link PasspointConfiguration} to the output stream as a XML block.
+ *
+ * @param out The output stream to serialize to
+ * @param config The configuration to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ public static void serializePasspointConfiguration(XmlSerializer out,
+ PasspointConfiguration config) throws XmlPullParserException, IOException {
+ XmlUtil.writeNextValue(out, XML_TAG_UPDATE_IDENTIFIER, config.getUpdateIdentifier());
+ XmlUtil.writeNextValue(out, XML_TAG_CREDENTIAL_PRIORITY, config.getCredentialPriority());
+ XmlUtil.writeNextValue(out, XML_TAG_TRUST_ROOT_CERT_LIST, config.getTrustRootCertList());
+ XmlUtil.writeNextValue(out, XML_TAG_SUBSCRIPTION_CREATION_TIME,
+ config.getSubscriptionCreationTimeInMillis());
+ XmlUtil.writeNextValue(out, XML_TAG_SUBSCRIPTION_EXPIRATION_TIME,
+ config.getSubscriptionExpirationTimeInMillis());
+ XmlUtil.writeNextValue(out, XML_TAG_SUBSCRIPTION_TYPE, config.getSubscriptionType());
+ XmlUtil.writeNextValue(out, XML_TAG_USAGE_LIMIT_TIME_PERIOD,
+ config.getUsageLimitUsageTimePeriodInMinutes());
+ XmlUtil.writeNextValue(out, XML_TAG_USAGE_LIMIT_START_TIME,
+ config.getUsageLimitStartTimeInMillis());
+ XmlUtil.writeNextValue(out, XML_TAG_USAGE_LIMIT_DATA_LIMIT,
+ config.getUsageLimitDataLimit());
+ XmlUtil.writeNextValue(out, XML_TAG_USAGE_LIMIT_TIME_LIMIT,
+ config.getUsageLimitTimeLimitInMinutes());
+ serializeHomeSp(out, config.getHomeSp());
+ serializeCredential(out, config.getCredential());
+ serializePolicy(out, config.getPolicy());
+ serializeUpdateParameter(out, XML_TAG_SECTION_HEADER_SUBSCRIPTION_UPDATE,
+ config.getSubscriptionUpdate());
+ }
+
+ /**
+ * Deserialize a {@link PasspointConfiguration} from an input stream containing XML block.
+ *
+ * @param in The input stream to read from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link PasspointConfiguration}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ public static PasspointConfiguration deserializePasspointConfiguration(XmlPullParser in,
+ int outerTagDepth) throws XmlPullParserException, IOException {
+ PasspointConfiguration config = new PasspointConfiguration();
+ while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
+ if (isValueElement(in)) {
+ // Value elements.
+ String[] name = new String[1];
+ Object value = XmlUtil.readCurrentValue(in, name);
+ switch (name[0]) {
+ case XML_TAG_UPDATE_IDENTIFIER:
+ config.setUpdateIdentifier((int) value);
+ break;
+ case XML_TAG_CREDENTIAL_PRIORITY:
+ config.setCredentialPriority((int) value);
+ break;
+ case XML_TAG_TRUST_ROOT_CERT_LIST:
+ config.setTrustRootCertList((Map<String, byte[]>) value);
+ break;
+ case XML_TAG_SUBSCRIPTION_CREATION_TIME:
+ config.setSubscriptionCreationTimeInMillis((long) value);
+ break;
+ case XML_TAG_SUBSCRIPTION_EXPIRATION_TIME:
+ config.setSubscriptionExpirationTimeInMillis((long) value);
+ break;
+ case XML_TAG_SUBSCRIPTION_TYPE:
+ config.setSubscriptionType((String) value);
+ break;
+ case XML_TAG_USAGE_LIMIT_TIME_PERIOD:
+ config.setUsageLimitUsageTimePeriodInMinutes((long) value);
+ break;
+ case XML_TAG_USAGE_LIMIT_START_TIME:
+ config.setUsageLimitStartTimeInMillis((long) value);
+ break;
+ case XML_TAG_USAGE_LIMIT_DATA_LIMIT:
+ config.setUsageLimitDataLimit((long) value);
+ break;
+ case XML_TAG_USAGE_LIMIT_TIME_LIMIT:
+ config.setUsageLimitTimeLimitInMinutes((long) value);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown value under "
+ + "PasspointConfiguration: " + in.getName());
+ }
+ } else {
+ // Section elements.
+ switch (in.getName()) {
+ case XML_TAG_SECTION_HEADER_HOMESP:
+ config.setHomeSp(deserializeHomeSP(in, outerTagDepth + 1));
+ break;
+ case XML_TAG_SECTION_HEADER_CREDENTIAL:
+ config.setCredential(deserializeCredential(in, outerTagDepth + 1));
+ break;
+ case XML_TAG_SECTION_HEADER_POLICY:
+ config.setPolicy(deserializePolicy(in, outerTagDepth + 1));
+ break;
+ case XML_TAG_SECTION_HEADER_SUBSCRIPTION_UPDATE:
+ config.setSubscriptionUpdate(
+ deserializeUpdateParameter(in, outerTagDepth + 1));
+ break;
+ default:
+ throw new XmlPullParserException("Unknown section under "
+ + "PasspointConfiguration: " + in.getName());
+ }
+ }
+ }
+ return config;
+ }
+
+ /**
+ * Serialize a {@link HomeSp} to an output stream as a XML block.
+ *
+ * @param out The output stream to serialize data to
+ * @param homeSp The {@link HomeSp} to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void serializeHomeSp(XmlSerializer out, HomeSp homeSp)
+ throws XmlPullParserException, IOException {
+ if (homeSp == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_HOMESP);
+ XmlUtil.writeNextValue(out, XML_TAG_FQDN, homeSp.getFqdn());
+ XmlUtil.writeNextValue(out, XML_TAG_FRIENDLY_NAME, homeSp.getFriendlyName());
+ XmlUtil.writeNextValue(out, XML_TAG_ICON_URL, homeSp.getIconUrl());
+ XmlUtil.writeNextValue(out, XML_TAG_HOME_NETWORK_IDS, homeSp.getHomeNetworkIds());
+ XmlUtil.writeNextValue(out, XML_TAG_MATCH_ALL_OIS, homeSp.getMatchAllOis());
+ XmlUtil.writeNextValue(out, XML_TAG_MATCH_ANY_OIS, homeSp.getMatchAnyOis());
+ XmlUtil.writeNextValue(out, XML_TAG_OTHER_HOME_PARTNERS, homeSp.getOtherHomePartners());
+ XmlUtil.writeNextValue(out, XML_TAG_ROAMING_CONSORTIUM_OIS,
+ homeSp.getRoamingConsortiumOis());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_HOMESP);
+ }
+
+ /**
+ * Serialize a {@link Credential} to an output stream as a XML block.
+ *
+ * @param out The output stream to serialize to
+ * @param credential The {@link Credential} to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void serializeCredential(XmlSerializer out, Credential credential)
+ throws XmlPullParserException, IOException {
+ if (credential == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_CREDENTIAL);
+ XmlUtil.writeNextValue(out, XML_TAG_CREATION_TIME, credential.getCreationTimeInMillis());
+ XmlUtil.writeNextValue(out, XML_TAG_EXPIRATION_TIME,
+ credential.getExpirationTimeInMillis());
+ XmlUtil.writeNextValue(out, XML_TAG_REALM, credential.getRealm());
+ XmlUtil.writeNextValue(out, XML_TAG_CHECK_AAA_SERVER_CERT_STATUS,
+ credential.getCheckAaaServerCertStatus());
+ serializeUserCredential(out, credential.getUserCredential());
+ serializeCertCredential(out, credential.getCertCredential());
+ serializeSimCredential(out, credential.getSimCredential());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_CREDENTIAL);
+ }
+
+ /**
+ * Serialize a {@link Policy} to an output stream as a XML block.
+ *
+ * @param out The output stream to serialize to
+ * @param policy The {@link Policy} to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void serializePolicy(XmlSerializer out, Policy policy)
+ throws XmlPullParserException, IOException {
+ if (policy == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_POLICY);
+ XmlUtil.writeNextValue(out, XML_TAG_MIN_HOME_DOWNLINK_BANDWIDTH,
+ policy.getMinHomeDownlinkBandwidth());
+ XmlUtil.writeNextValue(out, XML_TAG_MIN_HOME_UPLINK_BANDWIDTH,
+ policy.getMinHomeUplinkBandwidth());
+ XmlUtil.writeNextValue(out, XML_TAG_MIN_ROAMING_DOWNLINK_BANDWIDTH,
+ policy.getMinRoamingDownlinkBandwidth());
+ XmlUtil.writeNextValue(out, XML_TAG_MIN_ROAMING_UPLINK_BANDWIDTH,
+ policy.getMinRoamingUplinkBandwidth());
+ XmlUtil.writeNextValue(out, XML_TAG_EXCLUDED_SSID_LIST, policy.getExcludedSsidList());
+ XmlUtil.writeNextValue(out, XML_TAG_MAXIMUM_BSS_LOAD_VALUE,
+ policy.getMaximumBssLoadValue());
+ serializeProtoPortMap(out, policy.getRequiredProtoPortMap());
+ serializeUpdateParameter(out, XML_TAG_SECTION_HEADER_POLICY_UPDATE,
+ policy.getPolicyUpdate());
+ serializePreferredRoamingPartnerList(out, policy.getPreferredRoamingPartnerList());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_POLICY);
+ }
+
+ /**
+ * Serialize a {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} to an output
+ * stream as a XML block.
+ *
+ * @param out The output stream to serialize data to
+ * @param userCredential The UserCredential to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void serializeUserCredential(XmlSerializer out,
+ Credential.UserCredential userCredential) throws XmlPullParserException, IOException {
+ if (userCredential == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_USER_CREDENTIAL);
+ XmlUtil.writeNextValue(out, XML_TAG_USERNAME, userCredential.getUsername());
+ XmlUtil.writeNextValue(out, XML_TAG_PASSWORD, userCredential.getPassword());
+ XmlUtil.writeNextValue(out, XML_TAG_MACHINE_MANAGED, userCredential.getMachineManaged());
+ XmlUtil.writeNextValue(out, XML_TAG_SOFT_TOKEN_APP, userCredential.getSoftTokenApp());
+ XmlUtil.writeNextValue(out, XML_TAG_ABLE_TO_SHARE, userCredential.getAbleToShare());
+ XmlUtil.writeNextValue(out, XML_TAG_EAP_TYPE, userCredential.getEapType());
+ XmlUtil.writeNextValue(out, XML_TAG_NON_EAP_INNER_METHOD,
+ userCredential.getNonEapInnerMethod());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_USER_CREDENTIAL);
+ }
+
+ /**
+ * Serialize a {@link android.net.wifi.hotspot2.pps.Credential.CertificateCredential} to an
+ * output stream as a XML block.
+ *
+ * @param out The output stream to serialize data to
+ * @param certCredential The CertificateCredential to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void serializeCertCredential(XmlSerializer out,
+ Credential.CertificateCredential certCredential)
+ throws XmlPullParserException, IOException {
+ if (certCredential == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_CERT_CREDENTIAL);
+ XmlUtil.writeNextValue(out, XML_TAG_CERT_TYPE, certCredential.getCertType());
+ XmlUtil.writeNextValue(out, XML_TAG_CERT_SHA256_FINGERPRINT,
+ certCredential.getCertSha256Fingerprint());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_CERT_CREDENTIAL);
+ }
+
+ /**
+ * Serialize a {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} to an
+ * output stream as a XML block.
+ *
+ * @param out The output stream to serialize data to
+ * @param simCredential The SimCredential to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void serializeSimCredential(XmlSerializer out,
+ Credential.SimCredential simCredential) throws XmlPullParserException, IOException {
+ if (simCredential == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_SIM_CREDENTIAL);
+ XmlUtil.writeNextValue(out, XML_TAG_IMSI, simCredential.getImsi());
+ XmlUtil.writeNextValue(out, XML_TAG_EAP_TYPE, simCredential.getEapType());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_SIM_CREDENTIAL);
+ }
+
+ /**
+ * Serialize a preferred roaming partner list to an output stream as a XML block.
+ *
+ * @param out The output stream to serialize data to
+ * @param preferredRoamingPartnerList The partner list to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void serializePreferredRoamingPartnerList(XmlSerializer out,
+ List<Policy.RoamingPartner> preferredRoamingPartnerList)
+ throws XmlPullParserException, IOException {
+ if (preferredRoamingPartnerList == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PREFERRED_ROAMING_PARTNER_LIST);
+ for (Policy.RoamingPartner partner : preferredRoamingPartnerList) {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_ROAMING_PARTNER);
+ XmlUtil.writeNextValue(out, XML_TAG_FQDN, partner.getFqdn());
+ XmlUtil.writeNextValue(out, XML_TAG_FQDN_EXACT_MATCH, partner.getFqdnExactMatch());
+ XmlUtil.writeNextValue(out, XML_TAG_PRIORITY, partner.getPriority());
+ XmlUtil.writeNextValue(out, XML_TAG_COUNTRIES, partner.getCountries());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_ROAMING_PARTNER);
+ }
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PREFERRED_ROAMING_PARTNER_LIST);
+ }
+
+ /**
+ * Serialize a {@link UpdateParameter} to an output stream as a XML block. The
+ * {@link UpdateParameter} are used for describing Subscription Update and Policy Update.
+ *
+ * @param out The output stream to serialize data to
+ * @param type The type the {@link UpdateParameter} is used for
+ * @param param The {@link UpdateParameter} to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void serializeUpdateParameter(XmlSerializer out, String type,
+ UpdateParameter param) throws XmlPullParserException, IOException {
+ if (param == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, type);
+ XmlUtil.writeNextValue(out, XML_TAG_UPDATE_INTERVAL, param.getUpdateIntervalInMinutes());
+ XmlUtil.writeNextValue(out, XML_TAG_UPDATE_METHOD, param.getUpdateMethod());
+ XmlUtil.writeNextValue(out, XML_TAG_RESTRICTION, param.getRestriction());
+ XmlUtil.writeNextValue(out, XML_TAG_SERVER_URI, param.getServerUri());
+ XmlUtil.writeNextValue(out, XML_TAG_USERNAME, param.getUsername());
+ XmlUtil.writeNextValue(out, XML_TAG_PASSWORD, param.getBase64EncodedPassword());
+ XmlUtil.writeNextValue(out, XML_TAG_TRUST_ROOT_CERT_URL, param.getTrustRootCertUrl());
+ XmlUtil.writeNextValue(out, XML_TAG_TRUST_ROOT_CERT_SHA256_FINGERPRINT,
+ param.getTrustRootCertSha256Fingerprint());
+ XmlUtil.writeNextSectionEnd(out, type);
+ }
+
+ /**
+ * Serialize a Protocol-to-Ports map to an output stream as a XML block. We're not able
+ * to use {@link XmlUtil#writeNextValue} to write this map, since that function only works for
+ * maps with String key.
+ *
+ * @param out The output stream to serialize data to
+ * @param protoPortMap The proto port map to serialize
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static void serializeProtoPortMap(XmlSerializer out, Map<Integer, String> protoPortMap)
+ throws XmlPullParserException, IOException {
+ if (protoPortMap == null) {
+ return;
+ }
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_REQUIRED_PROTO_PORT_MAP);
+ for (Map.Entry<Integer, String> entry : protoPortMap.entrySet()) {
+ XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_PROTO_PORT);
+ XmlUtil.writeNextValue(out, XML_TAG_PROTO, entry.getKey());
+ XmlUtil.writeNextValue(out, XML_TAG_PORTS, entry.getValue());
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_PROTO_PORT);
+ }
+ XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_REQUIRED_PROTO_PORT_MAP);
+ }
+
+ /**
+ * Deserialize a {@link HomeSp} from an input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link HomeSp}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static HomeSp deserializeHomeSP(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ HomeSp homeSp = new HomeSp();
+ 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_FQDN:
+ homeSp.setFqdn((String) value);
+ break;
+ case XML_TAG_FRIENDLY_NAME:
+ homeSp.setFriendlyName((String) value);
+ break;
+ case XML_TAG_ICON_URL:
+ homeSp.setIconUrl((String) value);
+ break;
+ case XML_TAG_HOME_NETWORK_IDS:
+ homeSp.setHomeNetworkIds((Map<String, Long>) value);
+ break;
+ case XML_TAG_MATCH_ALL_OIS:
+ homeSp.setMatchAllOis((long[]) value);
+ break;
+ case XML_TAG_MATCH_ANY_OIS:
+ homeSp.setMatchAnyOis((long[]) value);
+ break;
+ case XML_TAG_ROAMING_CONSORTIUM_OIS:
+ homeSp.setRoamingConsortiumOis((long[]) value);
+ break;
+ case XML_TAG_OTHER_HOME_PARTNERS:
+ homeSp.setOtherHomePartners((String[]) value);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown data under HomeSP: " + valueName[0]);
+ }
+ }
+ return homeSp;
+ }
+
+ /**
+ * Deserialize a {@link Credential} from an input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link Credential}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static Credential deserializeCredential(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ Credential credential = new Credential();
+ while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
+ if (isValueElement(in)) {
+ // Value elements.
+ String[] name = new String[1];
+ Object value = XmlUtil.readCurrentValue(in, name);
+ switch (name[0]) {
+ case XML_TAG_CREATION_TIME:
+ credential.setCreationTimeInMillis((long) value);
+ break;
+ case XML_TAG_EXPIRATION_TIME:
+ credential.setExpirationTimeInMillis((long) value);
+ break;
+ case XML_TAG_REALM:
+ credential.setRealm((String) value);
+ break;
+ case XML_TAG_CHECK_AAA_SERVER_CERT_STATUS:
+ credential.setCheckAaaServerCertStatus((boolean) value);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown value under Credential: "
+ + name[0]);
+ }
+ } else {
+ // Subsection elements.
+ switch (in.getName()) {
+ case XML_TAG_SECTION_HEADER_USER_CREDENTIAL:
+ credential.setUserCredential(
+ deserializeUserCredential(in, outerTagDepth + 1));
+ break;
+ case XML_TAG_SECTION_HEADER_CERT_CREDENTIAL:
+ credential.setCertCredential(
+ deserializeCertCredential(in, outerTagDepth + 1));
+ break;
+ case XML_TAG_SECTION_HEADER_SIM_CREDENTIAL:
+ credential.setSimCredential(
+ deserializeSimCredential(in, outerTagDepth + 1));
+ break;
+ default:
+ throw new XmlPullParserException("Unknown section under Credential: "
+ + in.getName());
+ }
+ }
+ }
+ return credential;
+ }
+
+ /**
+ * Deserialize a {@link Policy} from an input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link Policy}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static Policy deserializePolicy(XmlPullParser in, int outerTagDepth)
+ throws XmlPullParserException, IOException {
+ Policy policy = new Policy();
+ while (XmlUtils.nextElementWithin(in, outerTagDepth)) {
+ if (isValueElement(in)) {
+ // Value elements.
+ String[] name = new String[1];
+ Object value = XmlUtil.readCurrentValue(in, name);
+ switch (name[0]) {
+ case XML_TAG_MIN_HOME_DOWNLINK_BANDWIDTH:
+ policy.setMinHomeDownlinkBandwidth((long) value);
+ break;
+ case XML_TAG_MIN_HOME_UPLINK_BANDWIDTH:
+ policy.setMinHomeUplinkBandwidth((long) value);
+ break;
+ case XML_TAG_MIN_ROAMING_DOWNLINK_BANDWIDTH:
+ policy.setMinRoamingDownlinkBandwidth((long) value);
+ break;
+ case XML_TAG_MIN_ROAMING_UPLINK_BANDWIDTH:
+ policy.setMinRoamingUplinkBandwidth((long) value);
+ break;
+ case XML_TAG_EXCLUDED_SSID_LIST:
+ policy.setExcludedSsidList((String[]) value);
+ break;
+ case XML_TAG_MAXIMUM_BSS_LOAD_VALUE:
+ policy.setMaximumBssLoadValue((int) value);
+ break;
+ }
+ } else {
+ // Subsection elements.
+ switch (in.getName()) {
+ case XML_TAG_SECTION_HEADER_REQUIRED_PROTO_PORT_MAP:
+ policy.setRequiredProtoPortMap(
+ deserializeProtoPortMap(in, outerTagDepth + 1));
+ break;
+ case XML_TAG_SECTION_HEADER_POLICY_UPDATE:
+ policy.setPolicyUpdate(deserializeUpdateParameter(in, outerTagDepth + 1));
+ break;
+ case XML_TAG_SECTION_HEADER_PREFERRED_ROAMING_PARTNER_LIST:
+ policy.setPreferredRoamingPartnerList(
+ deserializePreferredRoamingPartnerList(in, outerTagDepth + 1));
+ break;
+ default:
+ throw new XmlPullParserException("Unknown section under Policy: "
+ + in.getName());
+ }
+ }
+ }
+ return policy;
+ }
+
+ /**
+ * Deserialize a {@link android.net.wifi.hotspot2.pps.Credential.UserCredential} from an
+ * input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link android.net.wifi.hotspot2.pps.Credential.UserCredential}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static Credential.UserCredential deserializeUserCredential(XmlPullParser in,
+ int outerTagDepth) throws XmlPullParserException, IOException {
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ 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_USERNAME:
+ userCredential.setUsername((String) value);
+ break;
+ case XML_TAG_PASSWORD:
+ userCredential.setPassword((String) value);
+ break;
+ case XML_TAG_MACHINE_MANAGED:
+ userCredential.setMachineManaged((boolean) value);
+ break;
+ case XML_TAG_SOFT_TOKEN_APP:
+ userCredential.setSoftTokenApp((String) value);
+ break;
+ case XML_TAG_ABLE_TO_SHARE:
+ userCredential.setAbleToShare((boolean) value);
+ break;
+ case XML_TAG_EAP_TYPE:
+ userCredential.setEapType((int) value);
+ break;
+ case XML_TAG_NON_EAP_INNER_METHOD:
+ userCredential.setNonEapInnerMethod((String) value);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown value under UserCredential: "
+ + valueName[0]);
+ }
+ }
+ return userCredential;
+ }
+
+ /**
+ * Deserialize a {@link android.net.wifi.hotspot2.pps.Credential.CertificateCredential}
+ * from an input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link android.net.wifi.hotspot2.pps.Credential.CertificateCredential}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static Credential.CertificateCredential deserializeCertCredential(XmlPullParser in,
+ int outerTagDepth) throws XmlPullParserException, IOException {
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ 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_CERT_TYPE:
+ certCredential.setCertType((String) value);
+ break;
+ case XML_TAG_CERT_SHA256_FINGERPRINT:
+ certCredential.setCertSha256Fingerprint((byte[]) value);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown value under CertCredential: "
+ + valueName[0]);
+ }
+ }
+ return certCredential;
+ }
+
+ /**
+ * Deserialize a {@link android.net.wifi.hotspot2.pps.Credential.SimCredential} from an
+ * input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link android.net.wifi.hotspot2.pps.Credential.SimCredential}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static Credential.SimCredential deserializeSimCredential(XmlPullParser in,
+ int outerTagDepth) throws XmlPullParserException, IOException {
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ 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_IMSI:
+ simCredential.setImsi((String) value);
+ break;
+ case XML_TAG_EAP_TYPE:
+ simCredential.setEapType((int) value);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown value under CertCredential: "
+ + valueName[0]);
+ }
+ }
+ return simCredential;
+ }
+
+ /**
+ * Deserialize a list of {@link android.net.wifi.hotspot2.pps.Policy.RoamingPartner} from an
+ * input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return List of {@link android.net.wifi.hotspot2.pps.Policy.RoamingPartner}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static List<Policy.RoamingPartner> deserializePreferredRoamingPartnerList(
+ XmlPullParser in, int outerTagDepth) throws XmlPullParserException, IOException {
+ List<Policy.RoamingPartner> roamingPartnerList = new ArrayList<>();
+ while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_ROAMING_PARTNER,
+ outerTagDepth)) {
+ roamingPartnerList.add(deserializeRoamingPartner(in, outerTagDepth + 1));
+ }
+ return roamingPartnerList;
+ }
+
+ /**
+ * Deserialize a {@link android.net.wifi.hotspot2.pps.Policy.RoamingPartner} from an input
+ * stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link android.net.wifi.hotspot2.pps.Policy.RoamingPartner}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static Policy.RoamingPartner deserializeRoamingPartner(XmlPullParser in,
+ int outerTagDepth) throws XmlPullParserException, IOException {
+ Policy.RoamingPartner partner = new Policy.RoamingPartner();
+ 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_FQDN:
+ partner.setFqdn((String) value);
+ break;
+ case XML_TAG_FQDN_EXACT_MATCH:
+ partner.setFqdnExactMatch((boolean) value);
+ break;
+ case XML_TAG_PRIORITY:
+ partner.setPriority((int) value);
+ break;
+ case XML_TAG_COUNTRIES:
+ partner.setCountries((String) value);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown value under RoamingPartner: "
+ + valueName[0]);
+ }
+ }
+ return partner;
+ }
+
+ /**
+ * Deserialize a {@link UpdateParameter} from an input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return {@link UpdateParameter}
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static UpdateParameter deserializeUpdateParameter(XmlPullParser in,
+ int outerTagDepth) throws XmlPullParserException, IOException {
+ UpdateParameter param = new UpdateParameter();
+ 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_UPDATE_INTERVAL:
+ param.setUpdateIntervalInMinutes((long) value);
+ break;
+ case XML_TAG_UPDATE_METHOD:
+ param.setUpdateMethod((String) value);
+ break;
+ case XML_TAG_RESTRICTION:
+ param.setRestriction((String) value);
+ break;
+ case XML_TAG_SERVER_URI:
+ param.setServerUri((String) value);
+ break;
+ case XML_TAG_USERNAME:
+ param.setUsername((String) value);
+ break;
+ case XML_TAG_PASSWORD:
+ param.setBase64EncodedPassword((String) value);
+ break;
+ case XML_TAG_TRUST_ROOT_CERT_URL:
+ param.setTrustRootCertUrl((String) value);
+ break;
+ case XML_TAG_TRUST_ROOT_CERT_SHA256_FINGERPRINT:
+ param.setTrustRootCertSha256Fingerprint((byte[]) value);
+ break;
+ default:
+ throw new XmlPullParserException("Unknown value under UpdateParameter: "
+ + valueName[0]);
+ }
+ }
+ return param;
+ }
+
+ /**
+ * Deserialize a Protocol-Port map from an input stream.
+ *
+ * @param in The input stream to read data from
+ * @param outerTagDepth The tag depth of the current XML section
+ * @return Proocol-Port map
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ private static Map<Integer, String> deserializeProtoPortMap(XmlPullParser in,
+ int outerTagDepth) throws XmlPullParserException, IOException {
+ Map<Integer, String> protoPortMap = new HashMap<>();
+ while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_PROTO_PORT,
+ outerTagDepth)) {
+ int proto = (int) XmlUtil.readNextValueWithName(in, XML_TAG_PROTO);
+ String ports = (String) XmlUtil.readNextValueWithName(in, XML_TAG_PORTS);
+ protoPortMap.put(proto, ports);
+ }
+ return protoPortMap;
+ }
+
+ /**
+ * Determine if the current element is a value or a section. The "name" attribute of the
+ * element is used as the indicator, when it is present, the element is considered a value
+ * element.
+ *
+ * Value element:
+ * <int name="test">12</int>
+ *
+ * Section element:
+ * <Test>
+ * ...
+ * </Test>
+ *
+ * @param in XML input stream
+ * @return true if the current element is a value
+ */
+ private static boolean isValueElement(XmlPullParser in) {
+ return in.getAttributeValue(null, "name") != null;
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java b/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java
deleted file mode 100644
index 698968e..0000000
--- a/service/java/com/android/server/wifi/hotspot2/SupplicantBridge.java
+++ /dev/null
@@ -1,489 +0,0 @@
-package com.android.server.wifi.hotspot2;
-
-import android.util.Base64;
-import android.util.Log;
-
-import com.android.server.wifi.ScanDetail;
-import com.android.server.wifi.WifiNative;
-import com.android.server.wifi.anqp.ANQPElement;
-import com.android.server.wifi.anqp.ANQPFactory;
-import com.android.server.wifi.anqp.Constants;
-import com.android.server.wifi.anqp.eap.AuthParam;
-import com.android.server.wifi.anqp.eap.EAP;
-import com.android.server.wifi.anqp.eap.EAPMethod;
-import com.android.server.wifi.hotspot2.pps.Credential;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class SupplicantBridge {
- private final WifiNative mSupplicantHook;
- private final SupplicantBridgeCallbacks mCallbacks;
- private final Map<Long, ScanDetail> mRequestMap = new HashMap<>();
-
- private static final int IconChunkSize = 1400; // 2K*3/4 - overhead
- private static final Map<String, Constants.ANQPElementType> sWpsNames = new HashMap<>();
-
- static {
- sWpsNames.put("anqp_venue_name", Constants.ANQPElementType.ANQPVenueName);
- sWpsNames.put("anqp_network_auth_type", Constants.ANQPElementType.ANQPNwkAuthType);
- sWpsNames.put("anqp_roaming_consortium", Constants.ANQPElementType.ANQPRoamingConsortium);
- sWpsNames.put("anqp_ip_addr_type_availability",
- Constants.ANQPElementType.ANQPIPAddrAvailability);
- sWpsNames.put("anqp_nai_realm", Constants.ANQPElementType.ANQPNAIRealm);
- sWpsNames.put("anqp_3gpp", Constants.ANQPElementType.ANQP3GPPNetwork);
- sWpsNames.put("anqp_domain_name", Constants.ANQPElementType.ANQPDomName);
- sWpsNames.put("hs20_operator_friendly_name", Constants.ANQPElementType.HSFriendlyName);
- sWpsNames.put("hs20_wan_metrics", Constants.ANQPElementType.HSWANMetrics);
- sWpsNames.put("hs20_connection_capability", Constants.ANQPElementType.HSConnCapability);
- sWpsNames.put("hs20_operating_class", Constants.ANQPElementType.HSOperatingclass);
- sWpsNames.put("hs20_osu_providers_list", Constants.ANQPElementType.HSOSUProviders);
- }
-
- /**
- * Interface to be implemented by the client to receive callbacks from SupplicantBridge.
- */
- public interface SupplicantBridgeCallbacks {
- /**
- * Response from supplicant bridge for the initiated request.
- * @param scanDetail
- * @param anqpElements
- */
- void notifyANQPResponse(
- ScanDetail scanDetail,
- Map<Constants.ANQPElementType, ANQPElement> anqpElements);
-
- /**
- * Notify failure.
- * @param bssid
- */
- void notifyIconFailed(long bssid);
- }
-
- public static boolean isAnqpAttribute(String line) {
- int split = line.indexOf('=');
- return split >= 0 && sWpsNames.containsKey(line.substring(0, split));
- }
-
- public SupplicantBridge(WifiNative supplicantHook, SupplicantBridgeCallbacks callbacks) {
- mSupplicantHook = supplicantHook;
- mCallbacks = callbacks;
- }
-
- public static Map<Constants.ANQPElementType, ANQPElement> parseANQPLines(List<String> lines) {
- if (lines == null) {
- return null;
- }
- Map<Constants.ANQPElementType, ANQPElement> elements = new HashMap<>(lines.size());
- for (String line : lines) {
- try {
- ANQPElement element = buildElement(line);
- if (element != null) {
- elements.put(element.getID(), element);
- }
- }
- catch (ProtocolException pe) {
- Log.e(Utils.hs2LogTag(SupplicantBridge.class), "Failed to parse ANQP: " + pe);
- }
- }
- return elements;
- }
-
- public boolean startANQP(ScanDetail scanDetail, List<Constants.ANQPElementType> elements) {
- String anqpGet = buildWPSQueryRequest(scanDetail.getNetworkDetail(), elements);
- if (anqpGet == null) {
- return false;
- }
- synchronized (mRequestMap) {
- mRequestMap.put(scanDetail.getNetworkDetail().getBSSID(), scanDetail);
- }
- String result = mSupplicantHook.doCustomSupplicantCommand(anqpGet);
- if (result != null && result.startsWith("OK")) {
- Log.d(Utils.hs2LogTag(getClass()), "ANQP initiated on "
- + scanDetail + " (" + anqpGet + ")");
- return true;
- }
- else {
- Log.d(Utils.hs2LogTag(getClass()), "ANQP failed on " +
- scanDetail + ": " + result);
- return false;
- }
- }
-
- public boolean doIconQuery(long bssid, String fileName) {
- String result = mSupplicantHook.doCustomSupplicantCommand("REQ_HS20_ICON " +
- Utils.macToString(bssid) + " " + fileName);
- return result != null && result.startsWith("OK");
- }
-
- public byte[] retrieveIcon(IconEvent iconEvent) throws IOException {
- byte[] iconData = new byte[iconEvent.getSize()];
- try {
- int offset = 0;
- while (offset < iconEvent.getSize()) {
- int size = Math.min(iconEvent.getSize() - offset, IconChunkSize);
-
- String command = String.format("GET_HS20_ICON %s %s %d %d",
- Utils.macToString(iconEvent.getBSSID()), iconEvent.getFileName(),
- offset, size);
- Log.d(Utils.hs2LogTag(getClass()), "Issuing '" + command + "'");
- String response = mSupplicantHook.doCustomSupplicantCommand(command);
- if (response == null) {
- throw new IOException("No icon data returned");
- }
-
- try {
- byte[] fragment = Base64.decode(response, Base64.DEFAULT);
- if (fragment.length == 0) {
- throw new IOException("Null data for '" + command + "': " + response);
- }
- if (fragment.length + offset > iconData.length) {
- throw new IOException("Icon chunk exceeds image size");
- }
- System.arraycopy(fragment, 0, iconData, offset, fragment.length);
- offset += fragment.length;
- } catch (IllegalArgumentException iae) {
- throw new IOException("Failed to parse response to '" + command
- + "': " + response);
- }
- }
- if (offset != iconEvent.getSize()) {
- Log.w(Utils.hs2LogTag(getClass()), "Partial icon data: " + offset +
- ", expected " + iconEvent.getSize());
- }
- }
- finally {
- Log.d(Utils.hs2LogTag(getClass()), "Deleting icon for " + iconEvent);
- String result = mSupplicantHook.doCustomSupplicantCommand("DEL_HS20_ICON " +
- Utils.macToString(iconEvent.getBSSID()) + " " + iconEvent.getFileName());
- }
-
- return iconData;
- }
-
- public void notifyANQPDone(Long bssid, boolean success) {
- ScanDetail scanDetail;
- synchronized (mRequestMap) {
- scanDetail = mRequestMap.remove(bssid);
- }
-
- if (scanDetail == null) {
- if (!success) {
- mCallbacks.notifyIconFailed(bssid);
- }
- return;
- }
-
- String bssData = mSupplicantHook.scanResult(scanDetail.getBSSIDString());
- try {
- Map<Constants.ANQPElementType, ANQPElement> elements = parseWPSData(bssData);
- Log.d(Utils.hs2LogTag(getClass()), String.format("%s ANQP response for %012x: %s",
- success ? "successful" : "failed", bssid, elements));
- mCallbacks.notifyANQPResponse(scanDetail, success ? elements : null);
- }
- catch (IOException ioe) {
- Log.e(Utils.hs2LogTag(getClass()), "Failed to parse ANQP: " +
- ioe.toString() + ": " + bssData);
- }
- catch (RuntimeException rte) {
- Log.e(Utils.hs2LogTag(getClass()), "Failed to parse ANQP: " +
- rte.toString() + ": " + bssData, rte);
- }
- mCallbacks.notifyANQPResponse(scanDetail, null);
- }
-
- private static String escapeSSID(NetworkDetail networkDetail) {
- return escapeString(networkDetail.getSSID(), networkDetail.isSSID_UTF8());
- }
-
- private static String escapeString(String s, boolean utf8) {
- boolean asciiOnly = true;
- for (int n = 0; n < s.length(); n++) {
- char ch = s.charAt(n);
- if (ch > 127) {
- asciiOnly = false;
- break;
- }
- }
-
- if (asciiOnly) {
- return '"' + s + '"';
- }
- else {
- byte[] octets = s.getBytes(utf8 ? StandardCharsets.UTF_8 : StandardCharsets.ISO_8859_1);
-
- StringBuilder sb = new StringBuilder();
- for (byte octet : octets) {
- sb.append(String.format("%02x", octet & Constants.BYTE_MASK));
- }
- return sb.toString();
- }
- }
-
- /**
- * Build a wpa_supplicant ANQP query command
- * @param networkDetail The network to query.
- * @param querySet elements to query
- * @return A command string.
- */
- private static String buildWPSQueryRequest(NetworkDetail networkDetail,
- List<Constants.ANQPElementType> querySet) {
-
- boolean baseANQPElements = Constants.hasBaseANQPElements(querySet);
- StringBuilder sb = new StringBuilder();
- if (baseANQPElements) {
- sb.append("ANQP_GET ");
- }
- else {
- sb.append("HS20_ANQP_GET "); // ANQP_GET does not work for a sole hs20:8 (OSU) query
- }
- sb.append(networkDetail.getBSSIDString()).append(' ');
-
- boolean first = true;
- for (Constants.ANQPElementType elementType : querySet) {
- if (first) {
- first = false;
- }
- else {
- sb.append(',');
- }
-
- Integer id = Constants.getANQPElementID(elementType);
- if (id != null) {
- sb.append(id);
- }
- else {
- id = Constants.getHS20ElementID(elementType);
- if (baseANQPElements) {
- sb.append("hs20:");
- }
- sb.append(id);
- }
- }
-
- return sb.toString();
- }
-
- private static List<String> getWPSNetCommands(String netID, NetworkDetail networkDetail,
- Credential credential) {
-
- List<String> commands = new ArrayList<String>();
-
- EAPMethod eapMethod = credential.getEAPMethod();
- commands.add(String.format("SET_NETWORK %s key_mgmt WPA-EAP", netID));
- commands.add(String.format("SET_NETWORK %s ssid %s", netID, escapeSSID(networkDetail)));
- commands.add(String.format("SET_NETWORK %s bssid %s",
- netID, networkDetail.getBSSIDString()));
- commands.add(String.format("SET_NETWORK %s eap %s",
- netID, mapEAPMethodName(eapMethod.getEAPMethodID())));
-
- AuthParam authParam = credential.getEAPMethod().getAuthParam();
- if (authParam == null) {
- return null; // TLS or SIM/AKA
- }
- switch (authParam.getAuthInfoID()) {
- case NonEAPInnerAuthType:
- case InnerAuthEAPMethodType:
- commands.add(String.format("SET_NETWORK %s identity %s",
- netID, escapeString(credential.getUserName(), true)));
- commands.add(String.format("SET_NETWORK %s password %s",
- netID, escapeString(credential.getPassword(), true)));
- commands.add(String.format("SET_NETWORK %s anonymous_identity \"anonymous\"",
- netID));
- break;
- default: // !!! Needs work.
- return null;
- }
- commands.add(String.format("SET_NETWORK %s priority 0", netID));
- commands.add(String.format("ENABLE_NETWORK %s", netID));
- commands.add(String.format("SAVE_CONFIG"));
- return commands;
- }
-
- private static Map<Constants.ANQPElementType, ANQPElement> parseWPSData(String bssInfo)
- throws IOException {
- Map<Constants.ANQPElementType, ANQPElement> elements = new HashMap<>();
- if (bssInfo == null) {
- return elements;
- }
- BufferedReader lineReader = new BufferedReader(new StringReader(bssInfo));
- String line;
- while ((line=lineReader.readLine()) != null) {
- ANQPElement element = buildElement(line);
- if (element != null) {
- elements.put(element.getID(), element);
- }
- }
- return elements;
- }
-
- private static ANQPElement buildElement(String text) throws ProtocolException {
- int separator = text.indexOf('=');
- if (separator < 0) {
- return null;
- }
-
- String elementName = text.substring(0, separator);
- Constants.ANQPElementType elementType = sWpsNames.get(elementName);
- if (elementType == null) {
- return null;
- }
-
- byte[] payload;
- try {
- payload = Utils.hexToBytes(text.substring(separator + 1));
- }
- catch (NumberFormatException nfe) {
- Log.e(Utils.hs2LogTag(SupplicantBridge.class), "Failed to parse hex string");
- return null;
- }
- return Constants.getANQPElementID(elementType) != null ?
- ANQPFactory.buildElement(ByteBuffer.wrap(payload), elementType, payload.length) :
- ANQPFactory.buildHS20Element(elementType,
- ByteBuffer.wrap(payload).order(ByteOrder.LITTLE_ENDIAN));
- }
-
- private static String mapEAPMethodName(EAP.EAPMethodID eapMethodID) {
- switch (eapMethodID) {
- case EAP_AKA:
- return "AKA";
- case EAP_AKAPrim:
- return "AKA'"; // eap.c:1514
- case EAP_SIM:
- return "SIM";
- case EAP_TLS:
- return "TLS";
- case EAP_TTLS:
- return "TTLS";
- default:
- throw new IllegalArgumentException("No mapping for " + eapMethodID);
- }
- }
-
- private static final Map<Character,Integer> sMappings = new HashMap<Character, Integer>();
-
- static {
- sMappings.put('\\', (int)'\\');
- sMappings.put('"', (int)'"');
- sMappings.put('e', 0x1b);
- sMappings.put('n', (int)'\n');
- sMappings.put('r', (int)'\n');
- sMappings.put('t', (int)'\t');
- }
-
- public static String unescapeSSID(String ssid) {
-
- CharIterator chars = new CharIterator(ssid);
- byte[] octets = new byte[ssid.length()];
- int bo = 0;
-
- while (chars.hasNext()) {
- char ch = chars.next();
- if (ch != '\\' || ! chars.hasNext()) {
- octets[bo++] = (byte)ch;
- }
- else {
- char suffix = chars.next();
- Integer mapped = sMappings.get(suffix);
- if (mapped != null) {
- octets[bo++] = mapped.byteValue();
- }
- else if (suffix == 'x' && chars.hasDoubleHex()) {
- octets[bo++] = (byte)chars.nextDoubleHex();
- }
- else {
- octets[bo++] = '\\';
- octets[bo++] = (byte)suffix;
- }
- }
- }
-
- boolean asciiOnly = true;
- for (byte b : octets) {
- if ((b&0x80) != 0) {
- asciiOnly = false;
- break;
- }
- }
- if (asciiOnly) {
- return new String(octets, 0, bo, StandardCharsets.UTF_8);
- } else {
- try {
- // If UTF-8 decoding is successful it is almost certainly UTF-8
- CharBuffer cb = StandardCharsets.UTF_8.newDecoder().decode(
- ByteBuffer.wrap(octets, 0, bo));
- return cb.toString();
- } catch (CharacterCodingException cce) {
- return new String(octets, 0, bo, StandardCharsets.ISO_8859_1);
- }
- }
- }
-
- private static class CharIterator {
- private final String mString;
- private int mPosition;
- private int mHex;
-
- private CharIterator(String s) {
- mString = s;
- }
-
- private boolean hasNext() {
- return mPosition < mString.length();
- }
-
- private char next() {
- return mString.charAt(mPosition++);
- }
-
- private boolean hasDoubleHex() {
- if (mString.length() - mPosition < 2) {
- return false;
- }
- int nh = Utils.fromHex(mString.charAt(mPosition), true);
- if (nh < 0) {
- return false;
- }
- int nl = Utils.fromHex(mString.charAt(mPosition + 1), true);
- if (nl < 0) {
- return false;
- }
- mPosition += 2;
- mHex = (nh << 4) | nl;
- return true;
- }
-
- private int nextDoubleHex() {
- return mHex;
- }
- }
-
- private static final String[] TestStrings = {
- "test-ssid",
- "test\\nss\\tid",
- "test\\x2d\\x5f\\nss\\tid",
- "test\\x2d\\x5f\\nss\\tid\\\\",
- "test\\x2d\\x5f\\nss\\tid\\n",
- "test\\x2d\\x5f\\nss\\tid\\x4a",
- "another\\",
- "an\\other",
- "another\\x2"
- };
-
- public static void main(String[] args) {
- for (String string : TestStrings) {
- System.out.println(unescapeSSID(string));
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/Utils.java b/service/java/com/android/server/wifi/hotspot2/Utils.java
index 39488ff..dc04532 100644
--- a/service/java/com/android/server/wifi/hotspot2/Utils.java
+++ b/service/java/com/android/server/wifi/hotspot2/Utils.java
@@ -1,6 +1,6 @@
package com.android.server.wifi.hotspot2;
-import com.android.server.wifi.anqp.Constants;
+import com.android.server.wifi.hotspot2.anqp.Constants;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@@ -10,8 +10,8 @@
import java.util.List;
import java.util.TimeZone;
-import static com.android.server.wifi.anqp.Constants.BYTE_MASK;
-import static com.android.server.wifi.anqp.Constants.NIBBLE_MASK;
+import static com.android.server.wifi.hotspot2.anqp.Constants.BYTE_MASK;
+import static com.android.server.wifi.hotspot2.anqp.Constants.NIBBLE_MASK;
public abstract class Utils {
diff --git a/service/java/com/android/server/wifi/hotspot2/WnmData.java b/service/java/com/android/server/wifi/hotspot2/WnmData.java
new file mode 100644
index 0000000..97a7d11
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/WnmData.java
@@ -0,0 +1,74 @@
+/*
+ * 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.hotspot2;
+
+/**
+ * This class carries the payload of a Hotspot 2.0 Wireless Network Management (WNM) frame,
+ * described in the Hotspot 2.0 spec, section 3.2.
+ */
+public class WnmData {
+ public static final int ESS = 1; // HS2.0 spec section 3.2.1.2, table 4
+
+ private final long mBssid;
+ private final String mUrl;
+ private final boolean mDeauthEvent;
+ private final int mMethod;
+ private final boolean mEss;
+ private final int mDelay;
+
+ public WnmData(long bssid, String url, int method) {
+ mBssid = bssid;
+ mUrl = url;
+ mMethod = method;
+ mEss = false;
+ mDelay = -1;
+ mDeauthEvent = false;
+ }
+
+ public WnmData(long bssid, String url, boolean ess, int delay) {
+ mBssid = bssid;
+ mUrl = url;
+ mEss = ess;
+ mDelay = delay;
+ mMethod = -1;
+ mDeauthEvent = true;
+ }
+
+ public long getBssid() {
+ return mBssid;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public boolean isDeauthEvent() {
+ return mDeauthEvent;
+ }
+
+ public int getMethod() {
+ return mMethod;
+ }
+
+ public boolean isEss() {
+ return mEss;
+ }
+
+ public int getDelay() {
+ return mDelay;
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/ANQPElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/ANQPElement.java
similarity index 86%
rename from service/java/com/android/server/wifi/anqp/ANQPElement.java
rename to service/java/com/android/server/wifi/hotspot2/anqp/ANQPElement.java
index aec9537..b237b77 100644
--- a/service/java/com/android/server/wifi/anqp/ANQPElement.java
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/ANQPElement.java
@@ -1,4 +1,4 @@
-package com.android.server.wifi.anqp;
+package com.android.server.wifi.hotspot2.anqp;
/**
* Base class for an IEEE802.11u ANQP element.
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/ANQPParser.java b/service/java/com/android/server/wifi/hotspot2/anqp/ANQPParser.java
new file mode 100644
index 0000000..7a06ef4
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/ANQPParser.java
@@ -0,0 +1,135 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Factory to build a collection of 802.11u ANQP elements from a byte buffer.
+ */
+public class ANQPParser {
+ /**
+ * The OI value for Hotspot 2.0 ANQP-element.
+ */
+ @VisibleForTesting
+ public static final int VENDOR_SPECIFIC_HS20_OI = 0x506F9A;
+
+ /**
+ * The Type value for Hotspot 2.0 ANQP-element.
+ */
+ @VisibleForTesting
+ public static final int VENDOR_SPECIFIC_HS20_TYPE = 0x11;
+
+ /**
+ * Parse an ANQP element from the pass-in byte buffer.
+ *
+ * Note: Each Hotspot 2.0 Release 2 element will be wrapped inside a Vendor Specific element
+ * in the ANQP response from the AP. However, the lower layer (e.g. wpa_supplicant) should
+ * already take care of parsing those elements out of Vendor Specific elements. To be safe,
+ * we will parse the Vendor Specific elements for non-Hotspot 2.0 Release elements or in
+ * the case they're not parsed by the lower layer.
+ *
+ * @param infoID The ANQP element type
+ * @param payload The buffer to read from
+ * @return {@link com.android.server.wifi.hotspot2.anqp.ANQPElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static ANQPElement parseElement(Constants.ANQPElementType infoID, ByteBuffer payload)
+ throws ProtocolException {
+ switch (infoID) {
+ case ANQPVenueName:
+ return VenueNameElement.parse(payload);
+ case ANQPRoamingConsortium:
+ return RoamingConsortiumElement.parse(payload);
+ case ANQPIPAddrAvailability:
+ return IPAddressTypeAvailabilityElement.parse(payload);
+ case ANQPNAIRealm:
+ return NAIRealmElement.parse(payload);
+ case ANQP3GPPNetwork:
+ return ThreeGPPNetworkElement.parse(payload);
+ case ANQPDomName:
+ return DomainNameElement.parse(payload);
+ case ANQPVendorSpec:
+ return parseVendorSpecificElement(payload);
+ default:
+ throw new ProtocolException("Unknown element ID: " + infoID);
+ }
+ }
+
+ /**
+ * Parse a Hotspot 2.0 Release 2 ANQP element from the pass-in byte buffer.
+ *
+ * @param infoID The ANQP element ID
+ * @param payload The buffer to read from
+ * @return {@link com.android.server.wifi.hotspot2.anqp.ANQPElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static ANQPElement parseHS20Element(Constants.ANQPElementType infoID,
+ ByteBuffer payload) throws ProtocolException {
+ switch (infoID) {
+ case HSFriendlyName:
+ return HSFriendlyNameElement.parse(payload);
+ case HSWANMetrics:
+ return HSWanMetricsElement.parse(payload);
+ case HSConnCapability:
+ return HSConnectionCapabilityElement.parse(payload);
+ case HSOSUProviders:
+ return RawByteElement.parse(infoID, payload);
+ default:
+ throw new ProtocolException("Unknown element ID: " + infoID);
+ }
+ }
+
+ /**
+ * Parse the ANQP vendor specific element. Currently only supports the vendor specific
+ * element that contained Hotspot 2.0 ANQP-element.
+ *
+ * Format of a ANQP Vendor Specific element:
+ * | OI | Type | Subtype | Reserved | Payload |
+ * 3 1 1 1 variable
+ *
+ * @param payload The buffer to read from
+ * @return {@link ANQPElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ private static ANQPElement parseVendorSpecificElement(ByteBuffer payload)
+ throws ProtocolException {
+ int oi = (int) ByteBufferReader.readInteger(payload, ByteOrder.BIG_ENDIAN, 3);
+ int type = payload.get() & 0xFF;
+
+ if (oi != VENDOR_SPECIFIC_HS20_OI || type != VENDOR_SPECIFIC_HS20_TYPE) {
+ throw new ProtocolException("Unsupported vendor specific OI=" + oi + " type=" + type);
+ }
+
+ int subType = payload.get() & 0xFF;
+ Constants.ANQPElementType hs20ID = Constants.mapHS20Element(subType);
+ if (hs20ID == null) {
+ throw new ProtocolException("Unsupported subtype: " + subType);
+ }
+ payload.get(); // Skip the reserved byte
+ return parseHS20Element(hs20ID, payload);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/CellularNetwork.java b/service/java/com/android/server/wifi/hotspot2/anqp/CellularNetwork.java
new file mode 100644
index 0000000..cc39b3f
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/CellularNetwork.java
@@ -0,0 +1,167 @@
+/*
+ * 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.hotspot2.anqp;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The IEI (Information Element Identity) contained in the Generic Container for the
+ * 3GPP Cellular Network ANQP element.
+ *
+ * Refer to Annex A of 3GPP TS 24.234 version 11.3.0 for information on the data format:
+ * (http://www.etsi.org/deliver/etsi_ts/124200_124299/124234/11.03.00_60/ts_124234v110300p.pdf)
+ */
+public class CellularNetwork {
+ private static final String TAG = "CellularNetwork";
+
+ /**
+ * IEI type for PLMN (Public Land Mobile Network) list.
+ */
+ @VisibleForTesting
+ public static final int IEI_TYPE_PLMN_LIST = 0;
+
+ @VisibleForTesting
+ public static final int IEI_CONTENT_LENGTH_MASK = 0x7F;
+
+ /**
+ * Number of bytes for each PLMN (Public Land Mobile Network).
+ */
+ @VisibleForTesting
+ public static final int PLMN_DATA_BYTES = 3;
+
+ /**
+ * The value for comparing the third digit of MNC data with to determine if the MNC is
+ * two or three digits.
+ */
+ private static final int MNC_2DIGIT_VALUE = 0xF;
+
+ /**
+ * List of PLMN (Public Land Mobile Network) information.
+ */
+ private final List<String> mPlmnList;
+
+ @VisibleForTesting
+ public CellularNetwork(List<String> plmnList) {
+ mPlmnList = plmnList;
+ }
+
+ /**
+ * Parse a CellularNetwork from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link CellularNetwork}
+ * @throws ProtocolException
+ * @throws BufferUnderflowException
+ */
+ public static CellularNetwork parse(ByteBuffer payload) throws ProtocolException {
+ int ieiType = payload.get() & 0xFF;
+ int ieiSize = payload.get() & IEI_CONTENT_LENGTH_MASK;
+
+ // Skip this IEI if it is an unsupported type.
+ if (ieiType != IEI_TYPE_PLMN_LIST) {
+ Log.e(TAG, "Ignore unsupported IEI Type: " + ieiType);
+ // Advance the buffer position to the next IEI.
+ payload.position(payload.position() + ieiSize);
+ return null;
+ }
+
+ // Get PLMN count.
+ int plmnCount = payload.get() & 0xFF;
+
+ // Verify IEI size with PLMN count. The IEI size contained the PLMN count field plus
+ // the bytes for the PLMNs.
+ if (ieiSize != (plmnCount * PLMN_DATA_BYTES + 1)) {
+ throw new ProtocolException("IEI size and PLMN count mismatched: IEI Size=" + ieiSize
+ + " PLMN Count=" + plmnCount);
+ }
+
+ // Process each PLMN.
+ List<String> plmnList = new ArrayList<>();
+ while (plmnCount > 0) {
+ plmnList.add(parsePlmn(payload));
+ plmnCount--;
+ }
+ return new CellularNetwork(plmnList);
+ }
+
+ public List<String> getPlmns() {
+ return Collections.unmodifiableList(mPlmnList);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof CellularNetwork)) {
+ return false;
+ }
+ CellularNetwork that = (CellularNetwork) thatObject;
+ return mPlmnList.equals(that.mPlmnList);
+ }
+
+ @Override
+ public int hashCode() {
+ return mPlmnList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "CellularNetwork{mPlmnList=" + mPlmnList + "}";
+ }
+
+ /**
+ * Parse the PLMN information from the given buffer. A string representing a hex value
+ * of |MCC|MNC| will be returned.
+ *
+ * PLMN Coding Format:
+ * b7 b0
+ * | MCC Digit 2 | MCC Digit 1 |
+ * | MNC Digit 3 | MCC Digit 3 |
+ * | MNC Digit 2 | MNC Digit 1 |
+ *
+ * @param payload The buffer to read from.
+ * @return {@Link String}
+ * @throws BufferUnderflowException
+ */
+ private static String parsePlmn(ByteBuffer payload) {
+ byte[] plmn = new byte[PLMN_DATA_BYTES];
+ payload.get(plmn);
+
+ // Formatted as | MCC Digit 1 | MCC Digit 2 | MCC Digit 3 |
+ int mcc = ((plmn[0] << 8) & 0xF00) | (plmn[0] & 0x0F0) | (plmn[1] & 0x00F);
+
+ // Formated as |MNC Digit 1 | MNC Digit 2 |
+ int mnc = ((plmn[2] << 4) & 0xF0) | ((plmn[2] >> 4) & 0x0F);
+
+ // The digit 3 of MNC decides if the MNC is 2 or 3 digits number. When it is equal to
+ // 0xF, MNC is a 2 digit value. Otherwise, it is a 3 digit number.
+ int mncDigit3 = (plmn[1] >> 4) & 0x0F;
+ return (mncDigit3 != MNC_2DIGIT_VALUE)
+ ? String.format("%03x%03x", mcc, (mnc << 4) | mncDigit3)
+ : String.format("%03x%02x", mcc, mnc);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java b/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java
new file mode 100644
index 0000000..7cf34c7
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/Constants.java
@@ -0,0 +1,134 @@
+package com.android.server.wifi.hotspot2.anqp;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * ANQP related constants (802.11-2012)
+ */
+public class Constants {
+
+ public static final int NIBBLE_MASK = 0x0f;
+ public static final int BYTE_MASK = 0xff;
+ public static final int SHORT_MASK = 0xffff;
+ public static final long INT_MASK = 0xffffffffL;
+ public static final int BYTES_IN_SHORT = 2;
+ public static final int BYTES_IN_INT = 4;
+ public static final int BYTES_IN_EUI48 = 6;
+ public static final long MILLIS_IN_A_SEC = 1000L;
+
+ public static final int HS20_PREFIX = 0x119a6f50; // Note that this is represented as a LE int
+ public static final int HS20_FRAME_PREFIX = 0x109a6f50;
+ public static final int UTF8_INDICATOR = 1;
+
+ public static final int LANG_CODE_LENGTH = 3;
+ // From IEEE802.11-2012 section 8.4.1.34.
+ public static final int VENUE_INFO_LENGTH = 2;
+
+ public static final int ANQP_QUERY_LIST = 256;
+ public static final int ANQP_VENUE_NAME = 258;
+ public static final int ANQP_ROAMING_CONSORTIUM = 261;
+ public static final int ANQP_IP_ADDR_AVAILABILITY = 262;
+ public static final int ANQP_NAI_REALM = 263;
+ public static final int ANQP_3GPP_NETWORK = 264;
+ public static final int ANQP_DOM_NAME = 268;
+ public static final int ANQP_VENDOR_SPEC = 56797;
+
+ public static final int HS_QUERY_LIST = 1;
+ public static final int HS_FRIENDLY_NAME = 3;
+ public static final int HS_WAN_METRICS = 4;
+ public static final int HS_CONN_CAPABILITY = 5;
+ public static final int HS_NAI_HOME_REALM_QUERY = 6;
+ public static final int HS_OSU_PROVIDERS = 8;
+ public static final int HS_ICON_REQUEST = 10;
+
+ public enum ANQPElementType {
+ ANQPQueryList,
+ ANQPVenueName,
+ ANQPRoamingConsortium,
+ ANQPIPAddrAvailability,
+ ANQPNAIRealm,
+ ANQP3GPPNetwork,
+ ANQPDomName,
+ ANQPVendorSpec,
+ HSQueryList,
+ HSFriendlyName,
+ HSWANMetrics,
+ HSConnCapability,
+ HSNAIHomeRealmQuery,
+ HSOSUProviders,
+ HSIconRequest
+ }
+
+ private static final Map<Integer, ANQPElementType> sAnqpMap = new HashMap<>();
+ private static final Map<Integer, ANQPElementType> sHs20Map = new HashMap<>();
+ private static final Map<ANQPElementType, Integer> sRevAnqpmap =
+ new EnumMap<>(ANQPElementType.class);
+ private static final Map<ANQPElementType, Integer> sRevHs20map =
+ new EnumMap<>(ANQPElementType.class);
+
+ static {
+ sAnqpMap.put(ANQP_QUERY_LIST, ANQPElementType.ANQPQueryList);
+ sAnqpMap.put(ANQP_VENUE_NAME, ANQPElementType.ANQPVenueName);
+ sAnqpMap.put(ANQP_ROAMING_CONSORTIUM, ANQPElementType.ANQPRoamingConsortium);
+ sAnqpMap.put(ANQP_IP_ADDR_AVAILABILITY, ANQPElementType.ANQPIPAddrAvailability);
+ sAnqpMap.put(ANQP_NAI_REALM, ANQPElementType.ANQPNAIRealm);
+ sAnqpMap.put(ANQP_3GPP_NETWORK, ANQPElementType.ANQP3GPPNetwork);
+ sAnqpMap.put(ANQP_DOM_NAME, ANQPElementType.ANQPDomName);
+ sAnqpMap.put(ANQP_VENDOR_SPEC, ANQPElementType.ANQPVendorSpec);
+
+ sHs20Map.put(HS_QUERY_LIST, ANQPElementType.HSQueryList);
+ sHs20Map.put(HS_FRIENDLY_NAME, ANQPElementType.HSFriendlyName);
+ sHs20Map.put(HS_WAN_METRICS, ANQPElementType.HSWANMetrics);
+ sHs20Map.put(HS_CONN_CAPABILITY, ANQPElementType.HSConnCapability);
+ sHs20Map.put(HS_NAI_HOME_REALM_QUERY, ANQPElementType.HSNAIHomeRealmQuery);
+ sHs20Map.put(HS_OSU_PROVIDERS, ANQPElementType.HSOSUProviders);
+ sHs20Map.put(HS_ICON_REQUEST, ANQPElementType.HSIconRequest);
+
+ for (Map.Entry<Integer, ANQPElementType> entry : sAnqpMap.entrySet()) {
+ sRevAnqpmap.put(entry.getValue(), entry.getKey());
+ }
+ for (Map.Entry<Integer, ANQPElementType> entry : sHs20Map.entrySet()) {
+ sRevHs20map.put(entry.getValue(), entry.getKey());
+ }
+ }
+
+ public static ANQPElementType mapANQPElement(int id) {
+ return sAnqpMap.get(id);
+ }
+
+ public static ANQPElementType mapHS20Element(int id) {
+ return sHs20Map.get(id);
+ }
+
+ public static Integer getANQPElementID(ANQPElementType elementType) {
+ return sRevAnqpmap.get(elementType);
+ }
+
+ public static Integer getHS20ElementID(ANQPElementType elementType) {
+ return sRevHs20map.get(elementType);
+ }
+
+ public static boolean hasBaseANQPElements(Collection<ANQPElementType> elements) {
+ if (elements == null) {
+ return false;
+ }
+ for (ANQPElementType element : elements) {
+ if (sRevAnqpmap.containsKey(element)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean hasR2Elements(List<ANQPElementType> elements) {
+ return elements.contains(ANQPElementType.HSOSUProviders);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/DomainNameElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/DomainNameElement.java
new file mode 100644
index 0000000..35b3956
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/DomainNameElement.java
@@ -0,0 +1,93 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Domain Name ANQP Element, IEEE802.11-2012 section 8.4.4.15.
+ *
+ * Format:
+ * | Domain Name Field #1 (optional) | ...
+ * variable
+ *
+ * Domain Name Field Format:
+ * | Length | Domain Name |
+ * 1 variable
+ */
+public class DomainNameElement extends ANQPElement {
+ private final List<String> mDomains;
+
+ @VisibleForTesting
+ public DomainNameElement(List<String> domains) {
+ super(Constants.ANQPElementType.ANQPDomName);
+ mDomains = domains;
+ }
+
+ /**
+ * Parse a DomainNameElement from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link DomainNameElement}
+ * @throws BufferUnderflowException
+ */
+ public static DomainNameElement parse(ByteBuffer payload) {
+ List<String> domains = new ArrayList<>();
+ while (payload.hasRemaining()) {
+ // Use latin-1 to decode for now - safe for ASCII and retains encoding
+ domains.add(ByteBufferReader.readStringWithByteLength(
+ payload, StandardCharsets.ISO_8859_1));
+ }
+ return new DomainNameElement(domains);
+ }
+
+ public List<String> getDomains() {
+ return Collections.unmodifiableList(mDomains);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof DomainNameElement)) {
+ return false;
+ }
+ DomainNameElement that = (DomainNameElement) thatObject;
+ return mDomains.equals(that.mDomains);
+ }
+
+ @Override
+ public int hashCode() {
+ return mDomains.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "DomainName{" +
+ "mDomains=" + mDomains +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/GenericBlobElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/GenericBlobElement.java
similarity index 92%
rename from service/java/com/android/server/wifi/anqp/GenericBlobElement.java
rename to service/java/com/android/server/wifi/hotspot2/anqp/GenericBlobElement.java
index eff130d..f3279c8 100644
--- a/service/java/com/android/server/wifi/anqp/GenericBlobElement.java
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/GenericBlobElement.java
@@ -1,4 +1,4 @@
-package com.android.server.wifi.anqp;
+package com.android.server.wifi.hotspot2.anqp;
import com.android.server.wifi.hotspot2.Utils;
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElement.java
new file mode 100644
index 0000000..2c9a2b3
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElement.java
@@ -0,0 +1,87 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Connection Capability vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.5
+ *
+ * Format:
+ * | ProtoPort Tuple #1 (optiional) | ....
+ * 4
+ */
+public class HSConnectionCapabilityElement extends ANQPElement {
+ private final List<ProtocolPortTuple> mStatusList;
+
+ @VisibleForTesting
+ public HSConnectionCapabilityElement(List<ProtocolPortTuple> statusList) {
+ super(Constants.ANQPElementType.HSConnCapability);
+ mStatusList = statusList;
+ }
+
+ /**
+ * Parse a HSConnectionCapabilityElement from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link HSConnectionCapabilityElement}
+ * @throws BufferUnderflowException
+ */
+ public static HSConnectionCapabilityElement parse(ByteBuffer payload) {
+ List<ProtocolPortTuple> statusList = new ArrayList<>();
+ while (payload.hasRemaining()) {
+ statusList.add(ProtocolPortTuple.parse(payload));
+ }
+ return new HSConnectionCapabilityElement(statusList);
+ }
+
+ public List<ProtocolPortTuple> getStatusList() {
+ return Collections.unmodifiableList(mStatusList);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof HSConnectionCapabilityElement)) {
+ return false;
+ }
+ HSConnectionCapabilityElement that = (HSConnectionCapabilityElement) thatObject;
+ return mStatusList.equals(that.mStatusList);
+ }
+
+ @Override
+ public int hashCode() {
+ return mStatusList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "HSConnectionCapability{" +
+ "mStatusList=" + mStatusList +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElement.java
new file mode 100644
index 0000000..c6794c8
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElement.java
@@ -0,0 +1,94 @@
+package com.android.server.wifi.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Operator Friendly Name vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.3.
+ *
+ * Format:
+ *
+ * | Operator Name Duple #1 (optional) | ...
+ * variable
+ *
+ * | Operator Name Duple #N (optional) |
+ * variable
+ */
+public class HSFriendlyNameElement extends ANQPElement {
+ /**
+ * Maximum length for an Operator Name. Refer to Hotspot 2.0 (Release 2) Technical
+ * Specification section 4.3 for more info.
+ */
+ @VisibleForTesting
+ public static final int MAXIMUM_OPERATOR_NAME_LENGTH = 252;
+
+ private final List<I18Name> mNames;
+
+ @VisibleForTesting
+ public HSFriendlyNameElement(List<I18Name> names) {
+ super(Constants.ANQPElementType.HSFriendlyName);
+ mNames = names;
+ }
+
+ /**
+ * Parse a HSFriendlyNameElement from the given buffer.
+ *
+ * @param payload The buffer to read from
+ * @return {@link HSFriendlyNameElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static HSFriendlyNameElement parse(ByteBuffer payload)
+ throws ProtocolException {
+ List<I18Name> names = new ArrayList<I18Name>();
+ while (payload.hasRemaining()) {
+ I18Name name = I18Name.parse(payload);
+ // Verify that the number of bytes for the operator name doesn't exceed the max
+ // allowed.
+ int textBytes = name.getText().getBytes(StandardCharsets.UTF_8).length;
+ if (textBytes > MAXIMUM_OPERATOR_NAME_LENGTH) {
+ throw new ProtocolException("Operator Name exceeds the maximum allowed "
+ + textBytes);
+ }
+ names.add(name);
+ }
+ return new HSFriendlyNameElement(names);
+ }
+
+ public List<I18Name> getNames() {
+ return Collections.unmodifiableList(mNames);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof HSFriendlyNameElement)) {
+ return false;
+ }
+ HSFriendlyNameElement that = (HSFriendlyNameElement) thatObject;
+ return mNames.equals(that.mNames);
+ }
+
+ @Override
+ public int hashCode() {
+ return mNames.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "HSFriendlyName{" +
+ "mNames=" + mNames +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElement.java
new file mode 100644
index 0000000..b55fefb
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElement.java
@@ -0,0 +1,177 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * The WAN Metrics vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.4
+ *
+ * Format:
+ * | WAN Info | Downlink Speed | Uplink Speed | Downlink Load | Uplink Load | LMD |
+ * 1 4 4 1 1 2
+ *
+ * WAN Info Format:
+ * | Link Status | Symmetric Link | At Capacity | Reserved |
+ * B0 B1 B2 B3 B4 - B7
+ */
+public class HSWanMetricsElement extends ANQPElement {
+ public static final int LINK_STATUS_RESERVED = 0;
+ public static final int LINK_STATUS_UP = 1;
+ public static final int LINK_STATUS_DOWN = 2;
+ public static final int LINK_STATUS_TEST = 3;
+
+ @VisibleForTesting
+ public static final int EXPECTED_BUFFER_SIZE = 13;
+
+ @VisibleForTesting
+ public static final int LINK_STATUS_MASK = (1 << 0 | 1 << 1);
+
+ @VisibleForTesting
+ public static final int SYMMETRIC_LINK_MASK = 1 << 2;
+
+ @VisibleForTesting
+ public static final int AT_CAPACITY_MASK = 1 << 3;
+
+ private static final int MAX_LOAD = 256;
+
+ private final int mStatus;
+ private final boolean mSymmetric;
+ private final boolean mCapped;
+ private final long mDownlinkSpeed;
+ private final long mUplinkSpeed;
+ private final int mDownlinkLoad;
+ private final int mUplinkLoad;
+ private final int mLMD; // Load Measurement Duration.
+
+ @VisibleForTesting
+ public HSWanMetricsElement(int status, boolean symmetric, boolean capped, long downlinkSpeed,
+ long uplinkSpeed, int downlinkLoad, int uplinkLoad, int lmd) {
+ super(Constants.ANQPElementType.HSWANMetrics);
+ mStatus = status;
+ mSymmetric = symmetric;
+ mCapped = capped;
+ mDownlinkSpeed = downlinkSpeed;
+ mUplinkSpeed = uplinkSpeed;
+ mDownlinkLoad = downlinkLoad;
+ mUplinkLoad = uplinkLoad;
+ mLMD = lmd;
+ }
+
+ /**
+ * Parse a HSWanMetricsElement from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link HSWanMetricsElement}
+ * @throws ProtocolException
+ */
+ public static HSWanMetricsElement parse(ByteBuffer payload)
+ throws ProtocolException {
+ if (payload.remaining() != EXPECTED_BUFFER_SIZE) {
+ throw new ProtocolException("Unexpected buffer size: " + payload.remaining());
+ }
+
+ int wanInfo = payload.get() & 0xFF;
+ int status = wanInfo & LINK_STATUS_MASK;
+ boolean symmetric = (wanInfo & SYMMETRIC_LINK_MASK) != 0;
+ boolean capped = (wanInfo & AT_CAPACITY_MASK) != 0;
+ long downlinkSpeed = ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 4)
+ & 0xFFFFFFFFL;
+ long uplinkSpeed = ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 4)
+ & 0xFFFFFFFFL;
+ int downlinkLoad = payload.get() & 0xFF;
+ int uplinkLoad = payload.get() & 0xFF;
+ int lmd = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2) & 0xFFFF;
+ return new HSWanMetricsElement(status, symmetric, capped, downlinkSpeed, uplinkSpeed,
+ downlinkLoad, uplinkLoad, lmd);
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public boolean isSymmetric() {
+ return mSymmetric;
+ }
+
+ public boolean isCapped() {
+ return mCapped;
+ }
+
+ public long getDownlinkSpeed() {
+ return mDownlinkSpeed;
+ }
+
+ public long getUplinkSpeed() {
+ return mUplinkSpeed;
+ }
+
+ public int getDownlinkLoad() {
+ return mDownlinkLoad;
+ }
+
+ public int getUplinkLoad() {
+ return mUplinkLoad;
+ }
+
+ public int getLMD() {
+ return mLMD;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof HSWanMetricsElement)) {
+ return false;
+ }
+ HSWanMetricsElement that = (HSWanMetricsElement) thatObject;
+ return mStatus == that.mStatus
+ && mSymmetric == that.mSymmetric
+ && mCapped == that.mCapped
+ && mDownlinkSpeed == that.mDownlinkSpeed
+ && mUplinkSpeed == that.mUplinkSpeed
+ && mDownlinkLoad == that.mDownlinkLoad
+ && mUplinkLoad == that.mUplinkLoad
+ && mLMD == that.mLMD;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) (mStatus + mDownlinkSpeed + mUplinkSpeed + mDownlinkLoad
+ + mUplinkLoad + mLMD);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("HSWanMetrics{mStatus=%s, mSymmetric=%s, mCapped=%s, " +
+ "mDlSpeed=%d, mUlSpeed=%d, mDlLoad=%f, mUlLoad=%f, mLMD=%d}",
+ mStatus, mSymmetric, mCapped,
+ mDownlinkSpeed, mUplinkSpeed,
+ mDownlinkLoad * 100.0 / MAX_LOAD,
+ mUplinkLoad * 100.0 / MAX_LOAD,
+ mLMD);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/I18Name.java b/service/java/com/android/server/wifi/hotspot2/anqp/I18Name.java
new file mode 100644
index 0000000..3d44b0b
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/I18Name.java
@@ -0,0 +1,122 @@
+/*
+ * 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.hotspot2.anqp;
+
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+
+/**
+ * A generic Internationalized name field used in the Operator Friendly Name ANQP element
+ * (see HS2.0 R2 Spec 4.2) and the Venue Name ANQP element (see 802.11-2012 8.4.4.4).
+ *
+ * Format:
+ *
+ * | Length | Language Code | Name |
+ * 1 3 variable
+ */
+public class I18Name {
+ @VisibleForTesting
+ public static final int LANGUAGE_CODE_LENGTH = 3;
+
+ @VisibleForTesting
+ public static final int MINIMUM_LENGTH = LANGUAGE_CODE_LENGTH;
+
+ private final String mLanguage;
+ private final Locale mLocale;
+ private final String mText;
+
+ @VisibleForTesting
+ public I18Name(String language, Locale locale, String text) {
+ mLanguage = language;
+ mLocale = locale;
+ mText = text;
+ }
+
+ /**
+ * Parse a I18Name from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link I18Name}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static I18Name parse(ByteBuffer payload) throws ProtocolException {
+ // Retrieve the length field.
+ int length = payload.get() & 0xFF;
+
+ // Check for the minimum required length.
+ if (length < MINIMUM_LENGTH) {
+ throw new ProtocolException("Invalid length: " + length);
+ }
+
+ // Read the language string.
+ String language = ByteBufferReader.readString(
+ payload, LANGUAGE_CODE_LENGTH, StandardCharsets.US_ASCII).trim();
+ Locale locale = Locale.forLanguageTag(language);
+
+ // Read the text string.
+ String text = ByteBufferReader.readString(payload, length - LANGUAGE_CODE_LENGTH,
+ StandardCharsets.UTF_8);
+ return new I18Name(language, locale, text);
+ }
+
+ public String getLanguage() {
+ return mLanguage;
+ }
+
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof I18Name)) {
+ return false;
+ }
+
+ I18Name that = (I18Name) thatObject;
+ return TextUtils.equals(mLanguage, that.mLanguage)
+ && TextUtils.equals(mText, that.mText);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mLanguage.hashCode();
+ result = 31 * result + mText.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return mText + ':' + mLocale.getLanguage();
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElement.java
new file mode 100644
index 0000000..ed8f8c1
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElement.java
@@ -0,0 +1,149 @@
+package com.android.server.wifi.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The IP Address Type availability ANQP Element, IEEE802.11-2012 section 8.4.4.9
+ *
+ * Format:
+ *
+ * | IP Address |
+ * 1
+ * b0 b7
+ * | IPv6 Address | IPv4 Address |
+ * 2 bits 6 bits
+ *
+ * IPv4 Address field values:
+ * 0 - Address type not available
+ * 1 - Public IPv4 address available
+ * 2 - Port-restricted IPv4 address available
+ * 3 - Single NATed private IPv4 address available
+ * 4 - Single NATed private IPv4 address available
+ * 5 - Port-restricted IPv4 address and single NATed IPv4 address available
+ * 6 - Port-restricted IPv4 address and double NATed IPv4 address available
+ * 7 - Availability of the address type is not known
+ *
+ * IPv6 Address field values:
+ * 0 - Address type not available
+ * 1 - Address type not available
+ * 2 - Availability of the address type not known
+ *
+ */
+public class IPAddressTypeAvailabilityElement extends ANQPElement {
+ @VisibleForTesting
+ public static final int EXPECTED_BUFFER_LENGTH = 1;
+
+ /**
+ * Constants for IPv4 availability.
+ */
+ public static final int IPV4_NOT_AVAILABLE = 0;
+ public static final int IPV4_PUBLIC = 1;
+ public static final int IPV4_PORT_RESTRICTED = 2;
+ public static final int IPV4_SINGLE_NAT = 3;
+ public static final int IPV4_DOUBLE_NAT = 4;
+ public static final int IPV4_PORT_RESTRICTED_AND_SINGLE_NAT = 5;
+ public static final int IPV4_PORT_RESTRICTED_AND_DOUBLE_NAT = 6;
+ public static final int IPV4_UNKNOWN = 7;
+ private static final Set<Integer> IPV4_AVAILABILITY = new HashSet<Integer>();
+ static {
+ IPV4_AVAILABILITY.add(IPV4_NOT_AVAILABLE);
+ IPV4_AVAILABILITY.add(IPV4_PUBLIC);
+ IPV4_AVAILABILITY.add(IPV4_PORT_RESTRICTED);
+ IPV4_AVAILABILITY.add(IPV4_SINGLE_NAT);
+ IPV4_AVAILABILITY.add(IPV4_DOUBLE_NAT);
+ IPV4_AVAILABILITY.add(IPV4_PORT_RESTRICTED_AND_SINGLE_NAT);
+ IPV4_AVAILABILITY.add(IPV4_PORT_RESTRICTED_AND_DOUBLE_NAT);
+ }
+
+ /**
+ * Constants for IPv6 availability.
+ */
+ public static final int IPV6_NOT_AVAILABLE = 0;
+ public static final int IPV6_AVAILABLE = 1;
+ public static final int IPV6_UNKNOWN = 2;
+ private static final Set<Integer> IPV6_AVAILABILITY = new HashSet<Integer>();
+ static {
+ IPV6_AVAILABILITY.add(IPV6_NOT_AVAILABLE);
+ IPV6_AVAILABILITY.add(IPV6_AVAILABLE);
+ IPV6_AVAILABILITY.add(IPV6_UNKNOWN);
+ }
+
+ private static final int IPV4_AVAILABILITY_MASK = 0x3F;
+ private static final int IPV6_AVAILABILITY_MASK = 0x3;
+
+ private final int mV4Availability;
+ private final int mV6Availability;
+
+ @VisibleForTesting
+ public IPAddressTypeAvailabilityElement(int v4Availability, int v6Availability) {
+ super(Constants.ANQPElementType.ANQPIPAddrAvailability);
+ mV4Availability = v4Availability;
+ mV6Availability = v6Availability;
+ }
+
+ /**
+ * Parse an IPAddressTypeAvailabilityElement from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link IPAddressTypeAvailabilityElement}
+ * @throws ProtocolException
+ */
+ public static IPAddressTypeAvailabilityElement parse(ByteBuffer payload)
+ throws ProtocolException {
+ if (payload.remaining() != EXPECTED_BUFFER_LENGTH) {
+ throw new ProtocolException("Unexpected buffer length: " + payload.remaining());
+ }
+
+ int ipField = payload.get() & 0xFF;
+
+ int v6Availability = ipField & IPV6_AVAILABILITY_MASK;
+ if (!IPV6_AVAILABILITY.contains(v6Availability)) {
+ v6Availability = IPV6_UNKNOWN;
+ }
+
+ int v4Availability = (ipField >> 2) & IPV4_AVAILABILITY_MASK;
+ if (!IPV4_AVAILABILITY.contains(v4Availability)) {
+ v4Availability = IPV4_UNKNOWN;
+ }
+
+ return new IPAddressTypeAvailabilityElement(v4Availability, v6Availability);
+ }
+
+ public int getV4Availability() {
+ return mV4Availability;
+ }
+
+ public int getV6Availability() {
+ return mV6Availability;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof IPAddressTypeAvailabilityElement)) {
+ return false;
+ }
+ IPAddressTypeAvailabilityElement that = (IPAddressTypeAvailabilityElement) thatObject;
+ return mV4Availability == that.mV4Availability && mV6Availability == that.mV6Availability;
+ }
+
+ @Override
+ public int hashCode() {
+ return mV4Availability << 2 + mV6Availability;
+ }
+
+ @Override
+ public String toString() {
+ return "IPAddressTypeAvailability{" +
+ "mV4Availability=" + mV4Availability +
+ ", mV6Availability=" + mV6Availability +
+ '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/anqp/IconInfo.java b/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java
similarity index 81%
rename from service/java/com/android/server/wifi/anqp/IconInfo.java
rename to service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java
index 56fcf45..c961bbe 100644
--- a/service/java/com/android/server/wifi/anqp/IconInfo.java
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/IconInfo.java
@@ -1,11 +1,13 @@
-package com.android.server.wifi.anqp;
+package com.android.server.wifi.hotspot2.anqp;
import java.net.ProtocolException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
-import static com.android.server.wifi.anqp.Constants.SHORT_MASK;
+import static com.android.server.wifi.hotspot2.anqp.Constants.SHORT_MASK;
+
+import com.android.server.wifi.ByteBufferReader;
/**
* The Icons available OSU Providers sub field, as specified in
@@ -26,10 +28,10 @@
mWidth = payload.getShort() & SHORT_MASK;
mHeight = payload.getShort() & SHORT_MASK;
- mLanguage = Constants.getTrimmedString(payload,
- Constants.LANG_CODE_LENGTH, StandardCharsets.US_ASCII);
- mIconType = Constants.getPrefixedString(payload, 1, StandardCharsets.US_ASCII);
- mFileName = Constants.getPrefixedString(payload, 1, StandardCharsets.UTF_8);
+ mLanguage = ByteBufferReader.readString(
+ payload, Constants.LANG_CODE_LENGTH, StandardCharsets.US_ASCII).trim();
+ mIconType = ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.US_ASCII);
+ mFileName = ByteBufferReader.readStringWithByteLength(payload, StandardCharsets.UTF_8);
}
public int getWidth() {
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmData.java b/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmData.java
new file mode 100644
index 0000000..7b31449
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmData.java
@@ -0,0 +1,122 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The NAI Realm Data ANQP sub-element, IEEE802.11-2012 section 8.4.4.10 figure 8-418.
+ *
+ * Format:
+ * | Length | Encoding | NAIRealm Length | NAIRealm | EAPMethod Count | EAPMethod #1 (optional) |
+ * 2 1 1 variable 1 variable
+ */
+public class NAIRealmData {
+ /**
+ * Mask for determining NAI Realm String encoding type.
+ */
+ @VisibleForTesting
+ public static final int NAI_ENCODING_UTF8_MASK = 0x1;
+
+ @VisibleForTesting
+ public static final String NAI_REALM_STRING_SEPARATOR = ";";
+
+ private final List<String> mRealms;
+ private final List<EAPMethod> mEAPMethods;
+
+ @VisibleForTesting
+ public NAIRealmData(List<String> realms, List<EAPMethod> eapMethods) {
+ mRealms = realms;
+ mEAPMethods = eapMethods;
+ }
+
+ /**
+ * Parse a NAIRealmData from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link NAIRealmElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static NAIRealmData parse(ByteBuffer payload) throws ProtocolException {
+ // Read and verify the length field.
+ int length = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2)
+ & 0xFFFF;
+ if (length > payload.remaining()) {
+ throw new ProtocolException("Invalid data length: " + length);
+ }
+
+ // Read the encoding field.
+ boolean utf8 = (payload.get() & NAI_ENCODING_UTF8_MASK) != 0;
+
+ // Read the realm string.
+ String realm = ByteBufferReader.readStringWithByteLength(
+ payload, utf8 ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII);
+ List<String> realmList = Arrays.asList(realm.split(NAI_REALM_STRING_SEPARATOR));
+
+ // Read the EAP methods.
+ int methodCount = payload.get() & 0xFF;
+ List<EAPMethod> eapMethodList = new ArrayList<>();
+ while (methodCount > 0) {
+ eapMethodList.add(EAPMethod.parse(payload));
+ methodCount--;
+ }
+ return new NAIRealmData(realmList, eapMethodList);
+ }
+
+ public List<String> getRealms() {
+ return Collections.unmodifiableList(mRealms);
+ }
+
+ public List<EAPMethod> getEAPMethods() {
+ return Collections.unmodifiableList(mEAPMethods);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof NAIRealmData)) {
+ return false;
+ }
+ NAIRealmData that = (NAIRealmData) thatObject;
+ return mRealms.equals(that.mRealms) && mEAPMethods.equals(that.mEAPMethods);
+ }
+
+ @Override
+ public int hashCode() {
+ return mRealms.hashCode() * 31 + mEAPMethods.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "NAIRealmElement{mRealms=" + mRealms + " mEAPMethods=" + mEAPMethods + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmElement.java
new file mode 100644
index 0000000..6f18bad
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/NAIRealmElement.java
@@ -0,0 +1,93 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * The NAI (Network Access Identifier) Realm ANQP Element, IEEE802.11-2012 section 8.4.4.10.
+ *
+ * Format:
+ * | NAI Realm Count (optional) | NAI Realm Data #1 (optional) | ....
+ * 2 variable
+ */
+public class NAIRealmElement extends ANQPElement {
+ private final List<NAIRealmData> mRealmDataList;
+
+ @VisibleForTesting
+ public NAIRealmElement(List<NAIRealmData> realmDataList) {
+ super(Constants.ANQPElementType.ANQPNAIRealm);
+ mRealmDataList = realmDataList;
+ }
+
+ /**
+ * Parse a NAIRealmElement from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link NAIRealmElement}
+ * @throws BufferUnderflowException
+ */
+ public static NAIRealmElement parse(ByteBuffer payload)
+ throws ProtocolException {
+ List<NAIRealmData> realmDataList = new ArrayList<>();
+ if (payload.hasRemaining()) {
+ int count = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2)
+ & 0xFFFF;
+ while (count > 0) {
+ realmDataList.add(NAIRealmData.parse(payload));
+ count--;
+ }
+ }
+ return new NAIRealmElement(realmDataList);
+ }
+
+ public List<NAIRealmData> getRealmDataList() {
+ return Collections.unmodifiableList(mRealmDataList);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof NAIRealmElement)) {
+ return false;
+ }
+ NAIRealmElement that = (NAIRealmElement) thatObject;
+ return mRealmDataList.equals(that.mRealmDataList);
+ }
+
+ @Override
+ public int hashCode() {
+ return mRealmDataList.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "NAIRealmElement{mRealmDataList=" + mRealmDataList + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/ProtocolPortTuple.java b/service/java/com/android/server/wifi/hotspot2/anqp/ProtocolPortTuple.java
new file mode 100644
index 0000000..c097ad3
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/ProtocolPortTuple.java
@@ -0,0 +1,108 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * The ProtoPort Tuple used by Connection Capability vendor specific ANQP Element,
+ * Wi-Fi Alliance Hotspot 2.0 (Release 2) Technical Specification - Version 5.00,
+ * section 4.5
+ *
+ * Format:
+ * | IP Procotol | Port Number | Status |
+ * 1 2 1
+ */
+public class ProtocolPortTuple {
+ /**
+ * Number of raw bytes needed for the tuple.
+ */
+ @VisibleForTesting
+ public static final int RAW_BYTE_SIZE = 4;
+
+ public static final int PROTO_STATUS_CLOSED = 0;
+ public static final int PROTO_STATUS_OPEN = 1;
+ public static final int PROTO_STATUS_UNKNOWN = 2;
+
+ private final int mProtocol;
+ private final int mPort;
+ private final int mStatus;
+
+ @VisibleForTesting
+ public ProtocolPortTuple(int protocol, int port, int status) {
+ mProtocol = protocol;
+ mPort = port;
+ mStatus = status;
+ }
+
+ /**
+ * Parse a ProtocolPortTuple from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link ProtocolPortTuple}
+ * @throws BufferUnderflowException
+ */
+ public static ProtocolPortTuple parse(ByteBuffer payload) {
+ int protocol = payload.get();
+ int port = (int) ByteBufferReader.readInteger(payload, ByteOrder.LITTLE_ENDIAN, 2)
+ & 0xFFFF;
+ int status = payload.get() & 0xFF;
+ return new ProtocolPortTuple(protocol, port, status);
+ }
+
+ public int getProtocol() {
+ return mProtocol;
+ }
+
+ public int getPort() {
+ return mPort;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof ProtocolPortTuple)) {
+ return false;
+ }
+ ProtocolPortTuple that = (ProtocolPortTuple) thatObject;
+ return mProtocol == that.mProtocol
+ && mPort == that.mPort
+ && mStatus == that.mStatus;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mProtocol * 31 + mPort) * 31 + mStatus;
+ }
+
+ @Override
+ public String toString() {
+ return "ProtocolTuple{" + "mProtocol=" + mProtocol + ", mPort=" + mPort
+ + ", mStatus=" + mStatus + '}';
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/RawByteElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/RawByteElement.java
new file mode 100644
index 0000000..633147d
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/RawByteElement.java
@@ -0,0 +1,70 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * An object holding the raw octets of an ANQP element as provided by the wpa_supplicant.
+ */
+public class RawByteElement extends ANQPElement {
+ private final byte[] mPayload;
+
+ @VisibleForTesting
+ public RawByteElement(Constants.ANQPElementType infoID, byte[] payload) {
+ super(infoID);
+ mPayload = payload;
+ }
+
+ /**
+ * Parse a RawByteElement from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link HSConnectionCapabilityElement}
+ */
+ public static RawByteElement parse(Constants.ANQPElementType infoID, ByteBuffer payload) {
+ byte[] rawBytes = new byte[payload.remaining()];
+ if (payload.hasRemaining()) {
+ payload.get(rawBytes);
+ }
+ return new RawByteElement(infoID, rawBytes);
+ }
+
+ public byte[] getPayload() {
+ return mPayload;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof RawByteElement)) {
+ return false;
+ }
+ RawByteElement that = (RawByteElement) thatObject;
+ return getID() == that.getID() && Arrays.equals(mPayload, that.mPayload);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mPayload);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElement.java
new file mode 100644
index 0000000..a40e9d6
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElement.java
@@ -0,0 +1,88 @@
+package com.android.server.wifi.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+import com.android.server.wifi.hotspot2.Utils;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Roaming Consortium ANQP Element, IEEE802.11-2012 section 8.4.4.7
+ *
+ ** Format:
+ *
+ * | OI Duple #1 (optional) | ...
+ * variable
+ *
+ * | OI Length | OI |
+ * 1 variable
+ *
+ */
+public class RoamingConsortiumElement extends ANQPElement {
+ @VisibleForTesting
+ public static final int MINIMUM_OI_LENGTH = Byte.BYTES;
+
+ @VisibleForTesting
+ public static final int MAXIMUM_OI_LENGTH = Long.BYTES;
+
+ private final List<Long> mOIs;
+
+ @VisibleForTesting
+ public RoamingConsortiumElement(List<Long> ois) {
+ super(Constants.ANQPElementType.ANQPRoamingConsortium);
+ mOIs = ois;
+ }
+
+ /**
+ * Parse a VenueNameElement from the given payload.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link RoamingConsortiumElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static RoamingConsortiumElement parse(ByteBuffer payload)
+ throws ProtocolException {
+ List<Long> OIs = new ArrayList<Long>();
+ while (payload.hasRemaining()) {
+ int length = payload.get() & 0xFF;
+ if (length < MINIMUM_OI_LENGTH || length > MAXIMUM_OI_LENGTH) {
+ throw new ProtocolException("Bad OI length: " + length);
+ }
+ OIs.add(ByteBufferReader.readInteger(payload, ByteOrder.BIG_ENDIAN, length));
+ }
+ return new RoamingConsortiumElement(OIs);
+ }
+
+ public List<Long> getOIs() {
+ return Collections.unmodifiableList(mOIs);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof RoamingConsortiumElement)) {
+ return false;
+ }
+ RoamingConsortiumElement that = (RoamingConsortiumElement) thatObject;
+ return mOIs.equals(that.mOIs);
+ }
+
+ @Override
+ public int hashCode() {
+ return mOIs.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "RoamingConsortium{mOis=[" + Utils.roamingConsortiumsToString(mOIs) + "]}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElement.java
new file mode 100644
index 0000000..d9795c6
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElement.java
@@ -0,0 +1,115 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * The 3GPP Cellular Network ANQP Element, IEEE802.11-2012 section 8.4.4.11.
+ * The value is embedded in a Generic container User Data (GUD).
+ * Refer to Annex A of 3GPP TS 24.234 version 11.3.0 for more info:
+ * (http://www.etsi.org/deliver/etsi_ts/124200_124299/124234/11.03.00_60/ts_124234v110300p.pdf).
+ *
+ * Format:
+ * | GUD Version | Length | IEI 1 | ... | IEI N|
+ * 1 1 variable
+ *
+ */
+public class ThreeGPPNetworkElement extends ANQPElement {
+ /**
+ * The expected protocol version number of the Generic container User Data (GUD).
+ */
+ @VisibleForTesting
+ public static final int GUD_VERSION_1 = 0;
+
+ private final List<CellularNetwork> mNetworks;
+
+ @VisibleForTesting
+ public ThreeGPPNetworkElement(List<CellularNetwork> networks) {
+ super(Constants.ANQPElementType.ANQP3GPPNetwork);
+ mNetworks = networks;
+ }
+
+ /**
+ * Parse a ThreeGPPNetworkElement from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link ThreeGPPNetworkElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static ThreeGPPNetworkElement parse(ByteBuffer payload)
+ throws ProtocolException {
+ // Verify version.
+ int gudVersion = payload.get() & 0xFF;
+ if (gudVersion != GUD_VERSION_1) {
+ throw new ProtocolException("Unsupported GUD version: " + gudVersion);
+ }
+
+ // Verify length.
+ int length = payload.get() & 0xFF;
+ if (length != payload.remaining()) {
+ throw new ProtocolException("Mismatch length and buffer size: length=" + length
+ + " bufferSize=" + payload.remaining());
+ }
+
+ // Parse each IEI (Information Element Identity) content.
+ List<CellularNetwork> networks = new ArrayList<>();
+ while (payload.hasRemaining()) {
+ CellularNetwork network = CellularNetwork.parse(payload);
+ if (network != null) {
+ networks.add(network);
+ }
+ }
+ return new ThreeGPPNetworkElement(networks);
+ }
+
+ public List<CellularNetwork> getNetworks() {
+ return Collections.unmodifiableList(mNetworks);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof ThreeGPPNetworkElement)) {
+ return false;
+ }
+ ThreeGPPNetworkElement that = (ThreeGPPNetworkElement) thatObject;
+ return mNetworks.equals(that.mNetworks);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return mNetworks.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "ThreeGPPNetwork{mNetworks=" + mNetworks + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/VenueNameElement.java b/service/java/com/android/server/wifi/hotspot2/anqp/VenueNameElement.java
new file mode 100644
index 0000000..9a4e64b
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/VenueNameElement.java
@@ -0,0 +1,115 @@
+/*
+ * 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.hotspot2.anqp;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Venue Name ANQP Element, IEEE802.11-2012 section 8.4.4.4.
+ *
+ * Format:
+ *
+ * | Venue Info | Venue Name Duple #1 (optional) | ...
+ * 2 variable
+ *
+ * | Venue Name Duple #N (optional) |
+ * variable
+ *
+ * Refer to {@link I18Name} for the format of the Venue Name Duple
+ * fields.
+ */
+public class VenueNameElement extends ANQPElement {
+ @VisibleForTesting
+ public static final int VENUE_INFO_LENGTH = 2;
+
+ /**
+ * Maximum length for a Venue Name. Refer to IEEE802.11-2012 section 8.4.4.4 for more info.
+ */
+ @VisibleForTesting
+ public static final int MAXIMUM_VENUE_NAME_LENGTH = 252;
+
+ private final List<I18Name> mNames;
+
+ @VisibleForTesting
+ public VenueNameElement(List<I18Name> names) {
+ super(Constants.ANQPElementType.ANQPVenueName);
+ mNames = names;
+ }
+
+ /**
+ * Parse a VenueNameElement from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link VenueNameElement}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ public static VenueNameElement parse(ByteBuffer payload)
+ throws ProtocolException {
+ // Skip the Venue Info field, which we don't use.
+ for (int i = 0; i < VENUE_INFO_LENGTH; ++i) {
+ payload.get();
+ }
+
+ List<I18Name> names = new ArrayList<I18Name>();
+ while (payload.hasRemaining()) {
+ I18Name name = I18Name.parse(payload);
+ // Verify that the number of octets for the venue name doesn't exceed the max allowed.
+ int textBytes = name.getText().getBytes(StandardCharsets.UTF_8).length;
+ if (textBytes > MAXIMUM_VENUE_NAME_LENGTH) {
+ throw new ProtocolException("Venue Name exceeds the maximum allowed " + textBytes);
+ }
+ names.add(name);
+ }
+ return new VenueNameElement(names);
+ }
+
+ public List<I18Name> getNames() {
+ return Collections.unmodifiableList(mNames);
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (this == thatObject) {
+ return true;
+ }
+ if (!(thatObject instanceof VenueNameElement)) {
+ return false;
+ }
+ VenueNameElement that = (VenueNameElement) thatObject;
+ return mNames.equals(that.mNames);
+ }
+
+ @Override
+ public int hashCode() {
+ return mNames.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "VenueName{ mNames=" + mNames + "}";
+ }
+
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/eap/AuthParam.java b/service/java/com/android/server/wifi/hotspot2/anqp/eap/AuthParam.java
new file mode 100644
index 0000000..ce4edb2
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/eap/AuthParam.java
@@ -0,0 +1,41 @@
+/*
+ * 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.hotspot2.anqp.eap;
+
+/**
+ * An Authentication parameter, part of the NAI Realm ANQP element, specified in
+ * IEEE802.11-2012 section 8.4.4.10, table 8-188
+ */
+public abstract class AuthParam {
+ public static final int PARAM_TYPE_EXPANDED_EAP_METHOD = 1;
+ public static final int PARAM_TYPE_NON_EAP_INNER_AUTH_TYPE = 2;
+ public static final int PARAM_TYPE_INNER_AUTH_EAP_METHOD_TYPE = 3;
+ public static final int PARAM_TYPE_EXPANDED_INNER_EAP_METHOD = 4;
+ public static final int PARAM_TYPE_CREDENTIAL_TYPE = 5;
+ public static final int PARAM_TYPE_TUNNELED_EAP_METHOD_CREDENTIAL_TYPE = 6;
+ public static final int PARAM_TYPE_VENDOR_SPECIFIC = 221;
+
+ private final int mAuthTypeID;
+
+ protected AuthParam(int authTypeID) {
+ mAuthTypeID = authTypeID;
+ }
+
+ public int getAuthTypeID() {
+ return mAuthTypeID;
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/eap/CredentialType.java b/service/java/com/android/server/wifi/hotspot2/anqp/eap/CredentialType.java
new file mode 100644
index 0000000..1efc00b
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/eap/CredentialType.java
@@ -0,0 +1,103 @@
+/*
+ * 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.hotspot2.anqp.eap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * The Credential Type authentication parameter, IEEE802.11-2012, table 8-188.
+ * Used by both Credential Type and Tunneled EAP Method Credential Type authentication
+ * parameter.
+ *
+ * Format:
+ * | Type |
+ * 1
+ */
+public class CredentialType extends AuthParam {
+ public static final int CREDENTIAL_TYPE_SIM = 1;
+ public static final int CREDENTIAL_TYPE_USIM = 2;
+ public static final int CREDENTIAL_TYPE_NFC = 3;
+ public static final int CREDENTIAL_TYPE_HARDWARE_TOKEN = 4;
+ public static final int CREDENTIAL_TYPE_SOFTWARE_TOKEN = 5;
+ public static final int CREDENTIAL_TYPE_CERTIFICATE = 6;
+ public static final int CREDENTIAL_TYPE_USERNAME_PASSWORD = 7;
+ public static final int CREDENTIAL_TYPE_NONE = 8;
+ public static final int CREDENTIAL_TYPE_ANONYMOUS = 9;
+ public static final int CREDENTIAL_TYPE_VENDOR_SPECIFIC = 10;
+
+ @VisibleForTesting
+ public static final int EXPECTED_LENGTH_VALUE = 1;
+
+ private final int mType;
+
+ @VisibleForTesting
+ public CredentialType(int authType, int credType) {
+ super(authType);
+ mType = credType;
+ }
+
+ /**
+ * Parse a CredentialType from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @param length The length of the data
+ * @param tunneled Flag indicating if this is for a Tunneled EAP Method
+ * @return {@link CredentialType}
+ * @throws ProtocolException
+ * @throws BufferUnderflowException
+ */
+ public static CredentialType parse(ByteBuffer payload, int length, boolean tunneled)
+ throws ProtocolException {
+ if (length != EXPECTED_LENGTH_VALUE) {
+ throw new ProtocolException("Invalid length: " + length);
+ }
+ int credType = payload.get() & 0xFF;
+ int authType = tunneled ? AuthParam.PARAM_TYPE_TUNNELED_EAP_METHOD_CREDENTIAL_TYPE
+ : AuthParam.PARAM_TYPE_CREDENTIAL_TYPE;
+ return new CredentialType(authType, credType);
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ }
+ if (!(thatObject instanceof CredentialType)) {
+ return false;
+ }
+ CredentialType that = (CredentialType) thatObject;
+ return mType == that.mType;
+ }
+
+ @Override
+ public int hashCode() {
+ return mType;
+ }
+
+ @Override
+ public String toString() {
+ return "CredentialType{mType=" + mType + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/eap/EAPMethod.java b/service/java/com/android/server/wifi/hotspot2/anqp/eap/EAPMethod.java
new file mode 100644
index 0000000..6879d41
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/eap/EAPMethod.java
@@ -0,0 +1,154 @@
+/*
+ * 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.hotspot2.anqp.eap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An EAP Method part of the NAI Realm ANQP element, specified in
+ * IEEE802.11-2012 section 8.4.4.10, figure 8-420
+ *
+ * Format:
+ * | Length | EAP Method | Auth Param Count | Auth Param #1 (optional) | ....
+ * 1 1 1 variable
+ */
+public class EAPMethod {
+ private final int mEAPMethodID;
+ private final Map<Integer, Set<AuthParam>> mAuthParams;
+
+ @VisibleForTesting
+ public EAPMethod(int methodID, Map<Integer, Set<AuthParam>> authParams) {
+ mEAPMethodID = methodID;
+ mAuthParams = authParams;
+ }
+
+ /**
+ * Parse a EAPMethod from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link EAPMethod}
+ * @throws ProtocolException
+ * @throws BufferUnderflowException
+ */
+ public static EAPMethod parse(ByteBuffer payload) throws ProtocolException {
+ // Read and verify the length field.
+ int length = payload.get() & 0xFF;
+ if (length > payload.remaining()) {
+ throw new ProtocolException("Invalid data length: " + length);
+ }
+
+ int methodID = payload.get() & 0xFF;
+ int authCount = payload.get() & 0xFF;
+ Map<Integer, Set<AuthParam>> authParams = new HashMap<>();
+ while (authCount > 0) {
+ addAuthParam(authParams, parseAuthParam(payload));
+ authCount--;
+ }
+ return new EAPMethod(methodID, authParams);
+ }
+
+ /**
+ * Parse a AuthParam from the given buffer.
+ *
+ * Format:
+ * | Auth ID | Length | Value |
+ * 1 1 variable
+ *
+ * @param payload The byte buffer to read from
+ * @return {@link AuthParam}
+ * @throws BufferUnderflowException
+ * @throws ProtocolException
+ */
+ private static AuthParam parseAuthParam(ByteBuffer payload) throws ProtocolException {
+ int authID = payload.get() & 0xFF;
+ int length = payload.get() & 0xFF;
+ switch (authID) {
+ case AuthParam.PARAM_TYPE_EXPANDED_EAP_METHOD:
+ return ExpandedEAPMethod.parse(payload, length, false);
+ case AuthParam.PARAM_TYPE_NON_EAP_INNER_AUTH_TYPE:
+ return NonEAPInnerAuth.parse(payload, length);
+ case AuthParam.PARAM_TYPE_INNER_AUTH_EAP_METHOD_TYPE:
+ return InnerAuthEAP.parse(payload, length);
+ case AuthParam.PARAM_TYPE_EXPANDED_INNER_EAP_METHOD:
+ return ExpandedEAPMethod.parse(payload, length, true);
+ case AuthParam.PARAM_TYPE_CREDENTIAL_TYPE:
+ return CredentialType.parse(payload, length, false);
+ case AuthParam.PARAM_TYPE_TUNNELED_EAP_METHOD_CREDENTIAL_TYPE:
+ return CredentialType.parse(payload, length, true);
+ case AuthParam.PARAM_TYPE_VENDOR_SPECIFIC:
+ return VendorSpecificAuth.parse(payload, length);
+ default:
+ throw new ProtocolException("Unknow Auth Type ID: " + authID);
+ }
+ }
+
+ /**
+ * Add an AuthParam to a map of authentication parameters. It is possible to have
+ * multiple authentication parameters for the same type.
+ *
+ * @param paramsMap The authentication parameter map to add the new parameter to
+ * @param authParam The authentication parameter to add
+ */
+ private static void addAuthParam(Map<Integer, Set<AuthParam>> paramsMap,
+ AuthParam authParam) {
+ Set<AuthParam> authParams = paramsMap.get(authParam.getAuthTypeID());
+ if (authParams == null) {
+ authParams = new HashSet<>();
+ paramsMap.put(authParam.getAuthTypeID(), authParams);
+ }
+ authParams.add(authParam);
+ }
+
+ public Map<Integer, Set<AuthParam>> getAuthParams() {
+ return Collections.unmodifiableMap(mAuthParams);
+ }
+
+ public int getEAPMethodID() {
+ return mEAPMethodID;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ }
+ if (!(thatObject instanceof EAPMethod)) {
+ return false;
+ }
+ EAPMethod that = (EAPMethod) thatObject;
+ return mEAPMethodID == that.mEAPMethodID && mAuthParams.equals(that.mAuthParams);
+ }
+
+ @Override
+ public int hashCode() {
+ return mEAPMethodID * 31 + mAuthParams.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "EAPMethod{mEAPMethodID=" + mEAPMethodID + " mAuthParams=" + mAuthParams + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethod.java b/service/java/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethod.java
new file mode 100644
index 0000000..a2a303f
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethod.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.hotspot2.anqp.eap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ByteBufferReader;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * The Expanded EAP Method authentication parameter, IEEE802.11-2012, table 8-189.
+ * Used by both Expanded EAP Method and Expanded Inner EAP Method.
+ *
+ * Format:
+ * | Vendor ID | Vendor Type |
+ * 3 4
+ */
+public class ExpandedEAPMethod extends AuthParam {
+ public static final int EXPECTED_LENGTH_VALUE = 7;
+
+ private final int mVendorID;
+ private final long mVendorType;
+
+ @VisibleForTesting
+ public ExpandedEAPMethod(int authType, int vendorID, long vendorType) {
+ super(authType);
+ mVendorID = vendorID;
+ mVendorType = vendorType;
+ }
+
+ /**
+ * Parse a ExpandedEAPMethod from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @param length The length of the data
+ * @param inner Flag indicating if this is for an Inner EAP method
+ * @return {@link ExpandedEAPMethod}
+ * @throws ProtocolException
+ * @throws BufferUnderflowException
+ */
+ public static ExpandedEAPMethod parse(ByteBuffer payload, int length, boolean inner)
+ throws ProtocolException {
+ if (length != EXPECTED_LENGTH_VALUE) {
+ throw new ProtocolException("Invalid length value: " + length);
+ }
+
+ // Vendor ID and Vendor Type are expressed in big-endian byte order according to
+ // the spec.
+ int vendorID = (int) ByteBufferReader.readInteger(payload, ByteOrder.BIG_ENDIAN, 3)
+ & 0xFFFFFF;
+ long vendorType = ByteBufferReader.readInteger(payload, ByteOrder.BIG_ENDIAN, 4)
+ & 0xFFFFFFFF;
+
+ int authType = inner ? AuthParam.PARAM_TYPE_EXPANDED_INNER_EAP_METHOD
+ : AuthParam.PARAM_TYPE_EXPANDED_EAP_METHOD;
+ return new ExpandedEAPMethod(authType, vendorID, vendorType);
+ }
+
+ public int getVendorID() {
+ return mVendorID;
+ }
+
+ public long getVendorType() {
+ return mVendorType;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ }
+ if (!(thatObject instanceof ExpandedEAPMethod)) {
+ return false;
+ }
+ ExpandedEAPMethod that = (ExpandedEAPMethod) thatObject;
+ return mVendorID == that.mVendorID && mVendorType == that.mVendorType;
+ }
+
+ @Override
+ public int hashCode() {
+ return (mVendorID) * 31 + (int) mVendorType;
+ }
+
+ @Override
+ public String toString() {
+ return "ExpandedEAPMethod{mVendorID=" + mVendorID + " mVendorType=" + mVendorType + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAP.java b/service/java/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAP.java
new file mode 100644
index 0000000..a7dbdac
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAP.java
@@ -0,0 +1,86 @@
+/*
+ * 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.hotspot2.anqp.eap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * The Inner Authentication EAP Method Type authentication parameter, IEEE802.11-2012, table 8-188.
+ *
+ * Format:
+ * | EAP Method ID |
+ * 1
+ */
+public class InnerAuthEAP extends AuthParam {
+ @VisibleForTesting
+ public static final int EXPECTED_LENGTH_VALUE = 1;
+
+ private final int mEAPMethodID;
+
+ @VisibleForTesting
+ public InnerAuthEAP(int eapMethodID) {
+ super(AuthParam.PARAM_TYPE_INNER_AUTH_EAP_METHOD_TYPE);
+ mEAPMethodID = eapMethodID;
+ }
+
+ /**
+ * Parse a InnerAuthEAP from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @param length The length of the data
+ * @return {@link InnerAuthEAP}
+ * @throws ProtocolException
+ * @throws BufferUnderflowException
+ */
+ public static InnerAuthEAP parse(ByteBuffer payload, int length) throws ProtocolException {
+ if (length != EXPECTED_LENGTH_VALUE) {
+ throw new ProtocolException("Invalid length: " + length);
+ }
+ int eapMethodID = payload.get() & 0xFF;
+ return new InnerAuthEAP(eapMethodID);
+ }
+
+ public int getEAPMethodID() {
+ return mEAPMethodID;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ }
+ if (!(thatObject instanceof InnerAuthEAP)) {
+ return false;
+ }
+ InnerAuthEAP that = (InnerAuthEAP) thatObject;
+ return mEAPMethodID == that.mEAPMethodID;
+ }
+
+ @Override
+ public int hashCode() {
+ return mEAPMethodID;
+ }
+
+ @Override
+ public String toString() {
+ return "InnerAuthEAP{mEAPMethodID=" + mEAPMethodID + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuth.java b/service/java/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuth.java
new file mode 100644
index 0000000..b693393
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuth.java
@@ -0,0 +1,109 @@
+/*
+ * 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.hotspot2.anqp.eap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The Non-EAP Inner Authentication Type authentication parameter, IEEE802.11-2012, table 8-188.
+ *
+ * Format:
+ * | Type |
+ * 1
+ */
+public class NonEAPInnerAuth extends AuthParam {
+ public static final int AUTH_TYPE_UNKNOWN = 0;
+ public static final int AUTH_TYPE_PAP = 1;
+ public static final int AUTH_TYPE_CHAP = 2;
+ public static final int AUTH_TYPE_MSCHAP = 3;
+ public static final int AUTH_TYPE_MSCHAPV2 = 4;
+
+ private static final Map<String, Integer> AUTH_TYPE_MAP = new HashMap<>();
+ static {
+ AUTH_TYPE_MAP.put("PAP", AUTH_TYPE_PAP);
+ AUTH_TYPE_MAP.put("CHAP", AUTH_TYPE_CHAP);
+ AUTH_TYPE_MAP.put("MS-CHAP", AUTH_TYPE_MSCHAP);
+ AUTH_TYPE_MAP.put("MS-CHAP-V2", AUTH_TYPE_MSCHAPV2);
+ }
+
+ @VisibleForTesting
+ public static final int EXPECTED_LENGTH_VALUE = 1;
+
+ private final int mAuthType;
+
+ public NonEAPInnerAuth(int authType) {
+ super(AuthParam.PARAM_TYPE_NON_EAP_INNER_AUTH_TYPE);
+ mAuthType = authType;
+ }
+
+ /**
+ * Parse a NonEAPInnerAuth from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @param length The length of the data
+ * @return {@link NonEAPInnerAuth}
+ * @throws BufferUnderflowException
+ */
+ public static NonEAPInnerAuth parse(ByteBuffer payload, int length) throws ProtocolException {
+ if (length != EXPECTED_LENGTH_VALUE) {
+ throw new ProtocolException("Invalid length: " + length);
+ }
+ int authType = payload.get() & 0xFF;
+ return new NonEAPInnerAuth(authType);
+ }
+
+ /**
+ * Convert an authentication type string to an integer representation.
+ *
+ * @param typeStr The string of authentication type
+ * @return int
+ */
+ public static int getAuthTypeID(String typeStr) {
+ if (AUTH_TYPE_MAP.containsKey(typeStr)) {
+ return AUTH_TYPE_MAP.get(typeStr).intValue();
+ }
+ return AUTH_TYPE_UNKNOWN;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ }
+ if (!(thatObject instanceof NonEAPInnerAuth)) {
+ return false;
+ }
+ NonEAPInnerAuth that = (NonEAPInnerAuth) thatObject;
+ return mAuthType == that.mAuthType;
+ }
+
+ @Override
+ public int hashCode() {
+ return mAuthType;
+ }
+
+ @Override
+ public String toString() {
+ return "NonEAPInnerAuth{mAuthType=" + mAuthType + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuth.java b/service/java/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuth.java
new file mode 100644
index 0000000..048f0d6
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuth.java
@@ -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.
+ */
+
+package com.android.server.wifi.hotspot2.anqp.eap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * The Vendor Specific authentication parameter, IEEE802.11-2012, table 8-188.
+ *
+ * Format:
+ * | Data |
+ * variable
+ */
+public class VendorSpecificAuth extends AuthParam {
+ private final byte[] mData;
+
+ @VisibleForTesting
+ public VendorSpecificAuth(byte[] data) {
+ super(AuthParam.PARAM_TYPE_VENDOR_SPECIFIC);
+ mData = data;
+ }
+
+ /**
+ * Parse a VendorSpecificAuth from the given buffer.
+ *
+ * @param payload The byte buffer to read from
+ * @param length The length of the data
+ * @return {@link VendorSpecificAuth}
+ * @throws BufferUnderflowException
+ */
+ public static VendorSpecificAuth parse(ByteBuffer payload, int length) {
+ byte[] data = new byte[length];
+ payload.get(data);
+ return new VendorSpecificAuth(data);
+ }
+
+ public byte[] getData() {
+ return mData;
+ }
+
+ @Override
+ public boolean equals(Object thatObject) {
+ if (thatObject == this) {
+ return true;
+ }
+ if (!(thatObject instanceof VendorSpecificAuth)) {
+ return false;
+ }
+ VendorSpecificAuth that = (VendorSpecificAuth) thatObject;
+ return Arrays.equals(mData, that.mData);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mData);
+ }
+
+ @Override
+ public String toString() {
+ return "VendorSpecificAuth{mData=" + Arrays.toString(mData) + "}";
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/MOTree.java b/service/java/com/android/server/wifi/hotspot2/omadm/MOTree.java
deleted file mode 100644
index 63541ee..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/MOTree.java
+++ /dev/null
@@ -1,273 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import org.xml.sax.SAXException;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-
-public class MOTree {
- public static final String MgmtTreeTag = "MgmtTree";
-
- public static final String NodeTag = "Node";
- public static final String NodeNameTag = "NodeName";
- public static final String PathTag = "Path";
- public static final String ValueTag = "Value";
- public static final String RTPropTag = "RTProperties";
- public static final String TypeTag = "Type";
- public static final String DDFNameTag = "DDFName";
-
- private final String mUrn;
- private final String mDtdRev;
- private final OMAConstructed mRoot;
-
- public MOTree(XMLNode node, String urn) throws IOException, SAXException {
- Iterator<XMLNode> children = node.getChildren().iterator();
-
- String dtdRev = null;
-
- while (children.hasNext()) {
- XMLNode child = children.next();
- if (child.getTag().equals(OMAConstants.SyncMLVersionTag)) {
- dtdRev = child.getText();
- children.remove();
- break;
- }
- }
-
- mUrn = urn;
- mDtdRev = dtdRev;
-
- mRoot = new ManagementTreeRoot(node, dtdRev);
-
- for (XMLNode child : node.getChildren()) {
- buildNode(mRoot, child);
- }
- }
-
- public MOTree(String urn, String rev, OMAConstructed root) {
- mUrn = urn;
- mDtdRev = rev;
- mRoot = root;
- }
-
- /**
- * Build a Passpoint OMA-DM Management Object tree object.
- * @param urn The URN for the tree.
- * @param rev The DTD revision for the tree.
- * @param root The OMA-DM tree root, in all practical cases the PerProviderSubscription
- * node.
- * @return an MOTree object
- */
- public static MOTree buildMgmtTree(String urn, String rev, OMAConstructed root) {
- OMAConstructed realRoot;
- switch (urn) {
- case OMAConstants.PPS_URN:
- case OMAConstants.DevInfoURN:
- case OMAConstants.DevDetailURN:
- case OMAConstants.DevDetailXURN:
- realRoot = new ManagementTreeRoot(OMAConstants.OMAVersion);
- realRoot.addChild(root);
- return new MOTree(urn, rev, realRoot);
- default:
- return new MOTree(urn, rev, root);
- }
- }
-
- public static boolean hasMgmtTreeTag(String text) {
- for (int n = 0; n < text.length(); n++) {
- char ch = text.charAt(n);
- if (ch > ' ') {
- return text.regionMatches(true, n, '<' + MgmtTreeTag + '>',
- 0, MgmtTreeTag.length() + 2);
- }
- }
- return false;
- }
-
- private static class NodeData {
- private final String mName;
- private String mPath;
- private String mValue;
-
- private NodeData(String name) {
- mName = name;
- }
-
- private void setPath(String path) {
- mPath = path;
- }
-
- private void setValue(String value) {
- mValue = value;
- }
-
- public String getName() {
- return mName;
- }
-
- public String getPath() {
- return mPath;
- }
-
- public String getValue() {
- return mValue;
- }
- }
-
- private static void buildNode(OMANode parent, XMLNode node) throws IOException {
- if (!node.getTag().equals(NodeTag))
- throw new IOException("Node is a '" + node.getTag() + "' instead of a 'Node'");
-
- Map<String, XMLNode> checkMap = new HashMap<>(3);
- String context = null;
- List<NodeData> values = new ArrayList<>();
- List<XMLNode> children = new ArrayList<>();
-
- NodeData curValue = null;
-
- for (XMLNode child : node.getChildren()) {
- XMLNode old = checkMap.put(child.getTag(), child);
-
- switch (child.getTag()) {
- case NodeNameTag:
- if (curValue != null)
- throw new IOException(NodeNameTag + " not expected");
- curValue = new NodeData(child.getText());
-
- break;
- case PathTag:
- if (curValue == null || curValue.getPath() != null)
- throw new IOException(PathTag + " not expected");
- curValue.setPath(child.getText());
-
- break;
- case ValueTag:
- if (!children.isEmpty())
- throw new IOException(ValueTag + " in constructed node");
- if (curValue == null || curValue.getValue() != null)
- throw new IOException(ValueTag + " not expected");
- curValue.setValue(child.getText());
- values.add(curValue);
- curValue = null;
-
- break;
- case RTPropTag:
- if (old != null)
- throw new IOException("Duplicate " + RTPropTag);
- XMLNode typeNode = getNextNode(child, TypeTag);
- XMLNode ddfName = getNextNode(typeNode, DDFNameTag);
- context = ddfName.getText();
- if (context == null)
- throw new IOException("No text in " + DDFNameTag);
-
- break;
- case NodeTag:
- if (!values.isEmpty())
- throw new IOException("Scalar node " + node.getText() + " has Node child");
- children.add(child);
-
- break;
- }
- }
-
- if (values.isEmpty()) {
- if (curValue == null)
- throw new IOException("Missing name");
-
- OMANode subNode = parent.addChild(curValue.getName(),
- context, null, curValue.getPath());
-
- for (XMLNode child : children) {
- buildNode(subNode, child);
- }
- } else {
- if (!children.isEmpty())
- throw new IOException("Got both sub nodes and value(s)");
-
- for (NodeData nodeData : values) {
- parent.addChild(nodeData.getName(), context,
- nodeData.getValue(), nodeData.getPath());
- }
- }
- }
-
- private static XMLNode getNextNode(XMLNode node, String tag) throws IOException {
- if (node == null)
- throw new IOException("No node for " + tag);
- if (node.getChildren().size() != 1)
- throw new IOException("Expected " + node.getTag() + " to have exactly one child");
- XMLNode child = node.getChildren().iterator().next();
- if (!child.getTag().equals(tag))
- throw new IOException("Expected " + node.getTag() + " to have child '" + tag +
- "' instead of '" + child.getTag() + "'");
- return child;
- }
-
- public String getUrn() {
- return mUrn;
- }
-
- public String getDtdRev() {
- return mDtdRev;
- }
-
- public OMAConstructed getRoot() {
- return mRoot;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("MO Tree v").append(mDtdRev).append(", urn ").append(mUrn).append(")\n");
- sb.append(mRoot);
-
- return sb.toString();
- }
-
- public void marshal(OutputStream out) throws IOException {
- out.write("tree ".getBytes(StandardCharsets.UTF_8));
- OMAConstants.serializeString(mDtdRev, out);
- out.write(String.format("(%s)\n", mUrn).getBytes(StandardCharsets.UTF_8));
- mRoot.marshal(out, 0);
- }
-
- public static MOTree unmarshal(InputStream in) throws IOException {
- boolean strip = true;
- StringBuilder tree = new StringBuilder();
- for (; ; ) {
- int octet = in.read();
- if (octet < 0) {
- throw new FileNotFoundException();
- } else if (octet > ' ') {
- tree.append((char) octet);
- strip = false;
- } else if (!strip) {
- break;
- }
- }
- if (!tree.toString().equals("tree")) {
- throw new IOException("Not a tree: " + tree);
- }
-
- String version = OMAConstants.deserializeString(in);
- int next = in.read();
- if (next != '(') {
- throw new IOException("Expected URN in tree definition");
- }
- String urn = OMAConstants.readURN(in);
-
- OMAConstructed root = OMANode.unmarshal(in);
-
- return new MOTree(urn, version, root);
- }
-
- public String toXml() {
- StringBuilder sb = new StringBuilder();
- mRoot.toXml(sb);
- return sb.toString();
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/ManagementTreeRoot.java b/service/java/com/android/server/wifi/hotspot2/omadm/ManagementTreeRoot.java
deleted file mode 100644
index 83a8dc8..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/ManagementTreeRoot.java
+++ /dev/null
@@ -1,58 +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.hotspot2.omadm;
-
-import java.util.Map;
-
-/**
- * A specialized OMAConstructed OMA-DM node used as the MgmtTree root node in Passpoint
- * management trees.
- */
-public class ManagementTreeRoot extends OMAConstructed {
- private final String mDtdRev;
-
- public ManagementTreeRoot(XMLNode node, String dtdRev) {
- super(null, MOTree.MgmtTreeTag, null, new MultiValueMap<OMANode>(),
- node.getTextualAttributes());
- mDtdRev = dtdRev;
- }
-
- public ManagementTreeRoot(String dtdRev) {
- super(null, MOTree.MgmtTreeTag, null, "xmlns", OMAConstants.SyncML);
- mDtdRev = dtdRev;
- }
-
- @Override
- public void toXml(StringBuilder sb) {
- sb.append('<').append(MOTree.MgmtTreeTag);
- if (getAttributes() != null && !getAttributes().isEmpty()) {
- for (Map.Entry<String, String> avp : getAttributes().entrySet()) {
- sb.append(' ').append(avp.getKey()).append("=\"")
- .append(escape(avp.getValue())).append('"');
- }
- }
- sb.append(">\n");
-
- sb.append('<').append(OMAConstants.SyncMLVersionTag)
- .append('>').append(mDtdRev)
- .append("</").append(OMAConstants.SyncMLVersionTag).append(">\n");
- for (OMANode child : getChildren()) {
- child.toXml(sb);
- }
- sb.append("</").append(MOTree.MgmtTreeTag).append(">\n");
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/MultiValueMap.java b/service/java/com/android/server/wifi/hotspot2/omadm/MultiValueMap.java
deleted file mode 100644
index 21e2831..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/MultiValueMap.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-public class MultiValueMap<T> {
- private final Map<String, ArrayList<T>> mMap = new LinkedHashMap<>();
-
- public void put(String key, T value) {
- key = key.toLowerCase();
- ArrayList<T> values = mMap.get(key);
- if (values == null) {
- values = new ArrayList<>();
- mMap.put(key, values);
- }
- values.add(value);
- }
-
- public T get(String key) {
- key = key.toLowerCase();
- List<T> values = mMap.get(key);
- if (values == null) {
- return null;
- }
- else if (values.size() == 1) {
- return values.get(0);
- }
- else {
- throw new IllegalArgumentException("Cannot do get on multi-value");
- }
- }
-
- public T replace(String key, T oldValue, T newValue) {
- key = key.toLowerCase();
- List<T> values = mMap.get(key);
- if (values == null) {
- return null;
- }
-
- for (int n = 0; n < values.size(); n++) {
- T value = values.get(n);
- if (value == oldValue) {
- values.set(n, newValue);
- return value;
- }
- }
- return null;
- }
-
- public T remove(String key, T value) {
- key = key.toLowerCase();
- List<T> values = mMap.get(key);
- if (values == null) {
- return null;
- }
-
- T result = null;
- Iterator<T> valueIterator = values.iterator();
- while (valueIterator.hasNext()) {
- if (valueIterator.next() == value) {
- valueIterator.remove();
- result = value;
- break;
- }
- }
- if (values.isEmpty()) {
- mMap.remove(key);
- }
- return result;
- }
-
- public T remove(T value) {
- T result = null;
- Iterator<Map.Entry<String, ArrayList<T>>> iterator = mMap.entrySet().iterator();
- while (iterator.hasNext()) {
- ArrayList<T> values = iterator.next().getValue();
- Iterator<T> valueIterator = values.iterator();
- while (valueIterator.hasNext()) {
- if (valueIterator.next() == value) {
- valueIterator.remove();
- result = value;
- break;
- }
- }
- if (result != null) {
- if (values.isEmpty()) {
- iterator.remove();
- }
- break;
- }
- }
- return result;
- }
-
- public Collection<T> values() {
- List<T> allValues = new ArrayList<>(mMap.size());
- for (List<T> values : mMap.values()) {
- for (T value : values) {
- allValues.add(value);
- }
- }
- return allValues;
- }
-
- public T getSingletonValue() {
- if (mMap.size() != 1) {
- throw new IllegalArgumentException("Map is not a single entry map");
- }
- List<T> values = mMap.values().iterator().next();
- if (values.size() != 1) {
- throw new IllegalArgumentException("Map is not a single entry map");
- }
- return values.iterator().next();
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/NodeAttribute.java b/service/java/com/android/server/wifi/hotspot2/omadm/NodeAttribute.java
deleted file mode 100644
index 87ded86..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/NodeAttribute.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-public class NodeAttribute {
- private final String mName;
- private final String mType;
- private final String mValue;
-
- public NodeAttribute(String name, String type, String value) {
- mName = name;
- mType = type;
- mValue = value;
- }
-
- public String getName() {
- return mName;
- }
-
- public String getValue() {
- return mValue;
- }
-
- public String getType() {
- return mType;
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (this == thatObject) {
- return true;
- }
- if (thatObject == null || getClass() != thatObject.getClass()) {
- return false;
- }
-
- NodeAttribute that = (NodeAttribute) thatObject;
- return mName.equals(that.mName) && mType.equals(that.mType) && mValue.equals(that.mValue);
- }
-
- @Override
- public String toString() {
- return String.format("%s (%s) = '%s'", mName, mType, mValue);
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstants.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstants.java
deleted file mode 100644
index 6d00edd..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstants.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-
-public class OMAConstants {
- private OMAConstants() {
- }
-
- public static final String MOVersion = "1.0";
- public static final String PPS_URN = "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0";
- public static final String DevInfoURN = "urn:oma:mo:oma-dm-devinfo:1.0";
- public static final String DevDetailURN = "urn:oma:mo:oma-dm-devdetail:1.0";
- public static final String DevDetailXURN = "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0";
-
- public static final String[] SupportedMO_URNs = {
- PPS_URN, DevInfoURN, DevDetailURN, DevDetailXURN
- };
-
- public static final String SppMOAttribute = "spp:moURN";
- public static final String TAG_PostDevData = "spp:sppPostDevData";
- public static final String TAG_SupportedVersions = "spp:supportedSPPVersions";
- public static final String TAG_SupportedMOs = "spp:supportedMOList";
- public static final String TAG_UpdateResponse = "spp:sppUpdateResponse";
- public static final String TAG_MOContainer = "spp:moContainer";
- public static final String TAG_Version = "spp:sppVersion";
-
- public static final String TAG_SessionID = "spp:sessionID";
- public static final String TAG_Status = "spp:sppStatus";
- public static final String TAG_Error = "spp:sppError";
-
- public static final String SyncMLVersionTag = "VerDTD";
- public static final String OMAVersion = "1.2";
- public static final String SyncML = "syncml:dmddf1.2";
-
- private static final byte[] INDENT = new byte[1024];
-
- public static void serializeString(String s, OutputStream out) throws IOException {
- byte[] octets = s.getBytes(StandardCharsets.UTF_8);
- byte[] prefix = String.format("%x:", octets.length).getBytes(StandardCharsets.UTF_8);
- out.write(prefix);
- out.write(octets);
- }
-
- public static void indent(int level, OutputStream out) throws IOException {
- out.write(INDENT, 0, level);
- }
-
- public static String deserializeString(InputStream in) throws IOException {
- StringBuilder prefix = new StringBuilder();
- for (; ; ) {
- byte b = (byte) in.read();
- if (b == '.')
- return null;
- else if (b == ':')
- break;
- else if (b > ' ')
- prefix.append((char) b);
- }
- int length = Integer.parseInt(prefix.toString(), 16);
- byte[] octets = new byte[length];
- int offset = 0;
- while (offset < octets.length) {
- int amount = in.read(octets, offset, octets.length - offset);
- if (amount <= 0)
- throw new EOFException();
- offset += amount;
- }
- return new String(octets, StandardCharsets.UTF_8);
- }
-
- public static String readURN(InputStream in) throws IOException {
- StringBuilder urn = new StringBuilder();
-
- for (; ; ) {
- byte b = (byte) in.read();
- if (b == ')')
- break;
- urn.append((char) b);
- }
- return urn.toString();
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstructed.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstructed.java
deleted file mode 100644
index c0e4c66..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/OMAConstructed.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-
-public class OMAConstructed extends OMANode {
- private final MultiValueMap<OMANode> mChildren;
-
- public OMAConstructed(OMAConstructed parent, String name, String context, String ... avps) {
- this(parent, name, context, new MultiValueMap<OMANode>(), buildAttributes(avps));
- }
-
- protected OMAConstructed(OMAConstructed parent, String name, String context,
- MultiValueMap<OMANode> children, Map<String, String> avps) {
- super(parent, name, context, avps);
- mChildren = children;
- }
-
- @Override
- public OMANode addChild(String name, String context, String value, String pathString)
- throws IOException {
- if (pathString == null) {
- OMANode child = value != null ?
- new OMAScalar(this, name, context, value) :
- new OMAConstructed(this, name, context);
- mChildren.put(name, child);
- return child;
- } else {
- OMANode target = this;
- while (target.getParent() != null)
- target = target.getParent();
-
- for (String element : pathString.split("/")) {
- target = target.getChild(element);
- if (target == null)
- throw new IOException("No child node '" + element + "' in " + getPathString());
- else if (target.isLeaf())
- throw new IOException("Cannot add child to leaf node: " + getPathString());
- }
- return target.addChild(name, context, value, null);
- }
- }
-
- @Override
- public OMAConstructed reparent(OMAConstructed parent) {
- return new OMAConstructed(parent, getName(), getContext(), mChildren, getAttributes());
- }
-
- public void addChild(OMANode child) {
- mChildren.put(child.getName(), child.reparent(this));
- }
-
- public String getScalarValue(Iterator<String> path) throws OMAException {
- if (!path.hasNext()) {
- throw new OMAException("Path too short for " + getPathString());
- }
- String tag = path.next();
- OMANode child = mChildren.get(tag);
- if (child != null) {
- return child.getScalarValue(path);
- } else {
- return null;
- }
- }
-
- @Override
- public OMANode getListValue(Iterator<String> path) throws OMAException {
- if (!path.hasNext()) {
- return null;
- }
- String tag = path.next();
- OMANode child;
- if (tag.equals("?")) {
- child = mChildren.getSingletonValue();
- }
- else {
- child = mChildren.get(tag);
- }
-
- if (child == null) {
- return null;
- }
- else if (path.hasNext()) {
- return child.getListValue(path);
- } else {
- return child;
- }
- }
-
- @Override
- public boolean isLeaf() {
- return false;
- }
-
- @Override
- public Collection<OMANode> getChildren() {
- return Collections.unmodifiableCollection(mChildren.values());
- }
-
- public OMANode getChild(String name) {
- return mChildren.get(name);
- }
-
- public OMANode replaceNode(OMANode oldNode, OMANode newNode) {
- return mChildren.replace(oldNode.getName(), oldNode, newNode);
- }
-
- public OMANode removeNode(String key, OMANode node) {
- if (key.equals("?")) {
- return mChildren.remove(node);
- }
- else {
- return mChildren.remove(key, node);
- }
- }
-
- @Override
- public String getValue() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void toString(StringBuilder sb, int level) {
- sb.append(getPathString());
- if (getContext() != null) {
- sb.append(" (").append(getContext()).append(')');
- }
- sb.append('\n');
-
- for (OMANode node : mChildren.values()) {
- node.toString(sb, level + 1);
- }
- }
-
- @Override
- public void marshal(OutputStream out, int level) throws IOException {
- OMAConstants.indent(level, out);
- OMAConstants.serializeString(getName(), out);
- if (getContext() != null) {
- out.write(String.format("(%s)", getContext()).getBytes(StandardCharsets.UTF_8));
- }
- out.write(new byte[] { '+', '\n' });
-
- for (OMANode child : mChildren.values()) {
- child.marshal(out, level + 1);
- }
- OMAConstants.indent(level, out);
- out.write(".\n".getBytes(StandardCharsets.UTF_8));
- }
-
- @Override
- public void fillPayload(StringBuilder sb) {
- if (getContext() != null) {
- sb.append('<').append(MOTree.RTPropTag).append(">\n");
- sb.append('<').append(MOTree.TypeTag).append(">\n");
- sb.append('<').append(MOTree.DDFNameTag).append(">");
- sb.append(getContext());
- sb.append("</").append(MOTree.DDFNameTag).append(">\n");
- sb.append("</").append(MOTree.TypeTag).append(">\n");
- sb.append("</").append(MOTree.RTPropTag).append(">\n");
- }
-
- for (OMANode child : getChildren()) {
- child.toXml(sb);
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAException.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAException.java
deleted file mode 100644
index 5f8cd11..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/OMAException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import java.io.IOException;
-
-public class OMAException extends IOException {
- public OMAException(String message) {
- super(message);
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMANode.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMANode.java
deleted file mode 100644
index fbb6075..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/OMANode.java
+++ /dev/null
@@ -1,195 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-public abstract class OMANode {
- private final OMAConstructed mParent;
- private final String mName;
- private final String mContext;
- private final Map<String, String> mAttributes;
-
- private static final Map<Character, String> sEscapes = new HashMap<>();
-
- static {
- sEscapes.put('"', """);
- sEscapes.put('\'', "'");
- sEscapes.put('<', "<");
- sEscapes.put('>', ">");
- sEscapes.put('&', "&");
- }
-
- protected OMANode(OMAConstructed parent, String name, String context, Map<String, String> avps) {
- mParent = parent;
- mName = name;
- mContext = context;
- mAttributes = avps;
- }
-
- protected static Map<String, String> buildAttributes(String[] avps) {
- if (avps == null) {
- return null;
- }
- Map<String, String> attributes = new HashMap<>();
- for (int n = 0; n < avps.length; n += 2) {
- attributes.put(avps[n], avps[n+1]);
- }
- return attributes;
- }
-
- protected Map<String, String> getAttributes() {
- return mAttributes;
- }
-
- public OMAConstructed getParent() {
- return mParent;
- }
-
- public String getName() {
- return mName;
- }
-
- public String getContext() {
- return mContext;
- }
-
- public List<String> getPath() {
- LinkedList<String> path = new LinkedList<>();
- for (OMANode node = this; node != null; node = node.getParent()) {
- path.addFirst(node.getName());
- }
- return path;
- }
-
- public String getPathString() {
- StringBuilder sb = new StringBuilder();
- for (String element : getPath()) {
- sb.append('/').append(element);
- }
- return sb.toString();
- }
-
- /**
- * Perform escaping of special XML characters
- * @param s the raw string
- * @return a "XML clean" representation
- */
- public static String escape(String s) {
- StringBuilder sb = new StringBuilder(s.length());
- for (int n = 0; n < s.length(); n++) {
- char ch = s.charAt(n);
- String escape = sEscapes.get(ch);
- if (escape != null) {
- sb.append(escape);
- } else {
- sb.append(ch);
- }
- }
- return sb.toString();
- }
-
- public abstract OMANode reparent(OMAConstructed parent);
-
- public abstract String getScalarValue(Iterator<String> path) throws OMAException;
-
- public abstract OMANode getListValue(Iterator<String> path) throws OMAException;
-
- public abstract boolean isLeaf();
-
- public abstract Collection<OMANode> getChildren();
-
- public abstract OMANode getChild(String name) throws OMAException;
-
- public abstract String getValue();
-
- public abstract OMANode addChild(String name, String context, String value, String path)
- throws IOException;
-
- public abstract void marshal(OutputStream out, int level) throws IOException;
-
- public abstract void toString(StringBuilder sb, int level);
-
- public abstract void fillPayload(StringBuilder sb);
-
- public void toXml(StringBuilder sb) {
- sb.append('<').append(MOTree.NodeTag);
- if (mAttributes != null && !mAttributes.isEmpty()) {
- for (Map.Entry<String, String> avp : mAttributes.entrySet()) {
- sb.append(' ').append(avp.getKey()).append("=\"")
- .append(escape(avp.getValue())).append('"');
- }
- }
- sb.append(">\n");
-
- sb.append('<').append(MOTree.NodeNameTag).append('>');
- sb.append(getName());
- sb.append("</").append(MOTree.NodeNameTag).append(">\n");
-
- fillPayload(sb);
-
- sb.append("</").append(MOTree.NodeTag).append(">\n");
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- toString(sb, 0);
- return sb.toString();
- }
-
- public static OMAConstructed unmarshal(InputStream in) throws IOException {
- OMANode node = buildNode(in, null);
- if (node == null || node.isLeaf()) {
- throw new IOException("Bad OMA tree");
- }
- unmarshal(in, (OMAConstructed) node);
- return (OMAConstructed) node;
- }
-
- private static void unmarshal(InputStream in, OMAConstructed parent) throws IOException {
- for (; ; ) {
- OMANode node = buildNode(in, parent);
- if (node == null) {
- return;
- }
- else if (!node.isLeaf()) {
- unmarshal(in, (OMAConstructed) node);
- }
- }
- }
-
- private static OMANode buildNode(InputStream in, OMAConstructed parent) throws IOException {
- String name = OMAConstants.deserializeString(in);
- if (name == null) {
- return null;
- }
-
- String urn = null;
- int next = in.read();
- if (next == '(') {
- urn = OMAConstants.readURN(in);
- next = in.read();
- }
-
- if (next == '=') {
- String value = OMAConstants.deserializeString(in);
- return parent.addChild(name, urn, value, null);
- } else if (next == '+') {
- if (parent != null) {
- return parent.addChild(name, urn, null, null);
- } else {
- return new OMAConstructed(null, name, urn);
- }
- }
- else {
- throw new IOException("Parse error: expected = or + after node name");
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAParser.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAParser.java
deleted file mode 100644
index d39fa33..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/OMAParser.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import java.io.IOException;
-import java.io.StringReader;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Parses an OMA-DM XML tree.
- * OMA-DM = Open Mobile Association Device Management
- */
-public class OMAParser extends DefaultHandler {
- private XMLNode mRoot;
- private XMLNode mCurrent;
-
- public OMAParser() {
- mRoot = null;
- mCurrent = null;
- }
-
- public MOTree parse(String text, String urn) throws IOException, SAXException {
- if (text == null) {
- throw new IOException("Missing text string");
- }
- try {
- SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
- parser.parse(new InputSource(new StringReader(text)), this);
- return new MOTree(mRoot, urn);
- } catch (ParserConfigurationException pce) {
- throw new SAXException(pce);
- }
- }
-
- public XMLNode getRoot() {
- return mRoot;
- }
-
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes)
- throws SAXException {
- XMLNode parent = mCurrent;
-
- mCurrent = new XMLNode(mCurrent, qName, attributes);
-
- if (mRoot == null)
- mRoot = mCurrent;
- else
- parent.addChild(mCurrent);
- }
-
- @Override
- public void endElement(String uri, String localName, String qName) throws SAXException {
- if (!qName.equals(mCurrent.getTag()))
- throw new SAXException("End tag '" + qName + "' doesn't match current node: " +
- mCurrent);
-
- try {
- mCurrent.close();
- } catch (IOException ioe) {
- throw new SAXException("Failed to close element", ioe);
- }
-
- mCurrent = mCurrent.getParent();
- }
-
- @Override
- public void characters(char[] ch, int start, int length) throws SAXException {
- mCurrent.addText(ch, start, length);
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/OMAScalar.java b/service/java/com/android/server/wifi/hotspot2/omadm/OMAScalar.java
deleted file mode 100644
index 86740ac..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/OMAScalar.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-
-public class OMAScalar extends OMANode {
- private final String mValue;
-
- public OMAScalar(OMAConstructed parent, String name, String context, String value,
- String ... avps) {
- this(parent, name, context, value, buildAttributes(avps));
- }
-
- public OMAScalar(OMAConstructed parent, String name, String context, String value,
- Map<String, String> avps) {
- super(parent, name, context, avps);
- mValue = value;
- }
-
- @Override
- public OMAScalar reparent(OMAConstructed parent) {
- return new OMAScalar(parent, getName(), getContext(), mValue, getAttributes());
- }
-
- public String getScalarValue(Iterator<String> path) throws OMAException {
- return mValue;
- }
-
- @Override
- public OMANode getListValue(Iterator<String> path) throws OMAException {
- throw new OMAException("Scalar encountered in list path: " + getPathString());
- }
-
- @Override
- public boolean isLeaf() {
- return true;
- }
-
- @Override
- public Collection<OMANode> getChildren() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String getValue() {
- return mValue;
- }
-
- @Override
- public OMANode getChild(String name) throws OMAException {
- throw new OMAException("'" + getName() + "' is a scalar node");
- }
-
- @Override
- public OMANode addChild(String name, String context, String value, String path)
- throws IOException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void toString(StringBuilder sb, int level) {
- sb.append(getPathString()).append('=').append(mValue);
- if (getContext() != null) {
- sb.append(" (").append(getContext()).append(')');
- }
- sb.append('\n');
- }
-
- @Override
- public void marshal(OutputStream out, int level) throws IOException {
- OMAConstants.indent(level, out);
- OMAConstants.serializeString(getName(), out);
- out.write((byte) '=');
- OMAConstants.serializeString(getValue(), out);
- out.write((byte) '\n');
- }
-
- @Override
- public void fillPayload(StringBuilder sb) {
- sb.append('<').append(MOTree.ValueTag).append('>');
- sb.append(escape(mValue));
- sb.append("</").append(MOTree.ValueTag).append(">\n");
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/PasspointManagementObjectManager.java b/service/java/com/android/server/wifi/hotspot2/omadm/PasspointManagementObjectManager.java
deleted file mode 100644
index e967212..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/PasspointManagementObjectManager.java
+++ /dev/null
@@ -1,951 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import android.net.wifi.PasspointManagementObjectDefinition;
-import android.util.Base64;
-import android.util.Log;
-
-import com.android.server.wifi.IMSIParameter;
-import com.android.server.wifi.anqp.eap.EAP;
-import com.android.server.wifi.anqp.eap.EAPMethod;
-import com.android.server.wifi.anqp.eap.ExpandedEAPMethod;
-import com.android.server.wifi.anqp.eap.InnerAuthEAP;
-import com.android.server.wifi.anqp.eap.NonEAPInnerAuth;
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.hotspot2.pps.Credential;
-import com.android.server.wifi.hotspot2.pps.HomeSP;
-import com.android.server.wifi.hotspot2.pps.Policy;
-import com.android.server.wifi.hotspot2.pps.SubscriptionParameters;
-import com.android.server.wifi.hotspot2.pps.UpdateInfo;
-
-import org.xml.sax.SAXException;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TimeZone;
-
-/**
- * Handles provisioning of PerProviderSubscription data.
- */
-public class PasspointManagementObjectManager {
-
- public static final String TAG_AAAServerTrustRoot = "AAAServerTrustRoot";
- public static final String TAG_AbleToShare = "AbleToShare";
- public static final String TAG_CertificateType = "CertificateType";
- public static final String TAG_CertSHA256Fingerprint = "CertSHA256Fingerprint";
- public static final String TAG_CertURL = "CertURL";
- public static final String TAG_CheckAAAServerCertStatus = "CheckAAAServerCertStatus";
- public static final String TAG_Country = "Country";
- public static final String TAG_CreationDate = "CreationDate";
- public static final String TAG_Credential = "Credential";
- public static final String TAG_CredentialPriority = "CredentialPriority";
- public static final String TAG_DataLimit = "DataLimit";
- public static final String TAG_DigitalCertificate = "DigitalCertificate";
- public static final String TAG_DLBandwidth = "DLBandwidth";
- public static final String TAG_EAPMethod = "EAPMethod";
- public static final String TAG_EAPType = "EAPType";
- public static final String TAG_ExpirationDate = "ExpirationDate";
- public static final String TAG_Extension = "Extension";
- public static final String TAG_FQDN = "FQDN";
- public static final String TAG_FQDN_Match = "FQDN_Match";
- public static final String TAG_FriendlyName = "FriendlyName";
- public static final String TAG_HESSID = "HESSID";
- public static final String TAG_HomeOI = "HomeOI";
- public static final String TAG_HomeOIList = "HomeOIList";
- public static final String TAG_HomeOIRequired = "HomeOIRequired";
- public static final String TAG_HomeSP = "HomeSP";
- public static final String TAG_IconURL = "IconURL";
- public static final String TAG_IMSI = "IMSI";
- public static final String TAG_InnerEAPType = "InnerEAPType";
- public static final String TAG_InnerMethod = "InnerMethod";
- public static final String TAG_InnerVendorID = "InnerVendorID";
- public static final String TAG_InnerVendorType = "InnerVendorType";
- public static final String TAG_IPProtocol = "IPProtocol";
- public static final String TAG_MachineManaged = "MachineManaged";
- public static final String TAG_MaximumBSSLoadValue = "MaximumBSSLoadValue";
- public static final String TAG_MinBackhaulThreshold = "MinBackhaulThreshold";
- public static final String TAG_NetworkID = "NetworkID";
- public static final String TAG_NetworkType = "NetworkType";
- public static final String TAG_Other = "Other";
- public static final String TAG_OtherHomePartners = "OtherHomePartners";
- public static final String TAG_Password = "Password";
- public static final String TAG_PerProviderSubscription = "PerProviderSubscription";
- public static final String TAG_Policy = "Policy";
- public static final String TAG_PolicyUpdate = "PolicyUpdate";
- public static final String TAG_PortNumber = "PortNumber";
- public static final String TAG_PreferredRoamingPartnerList = "PreferredRoamingPartnerList";
- public static final String TAG_Priority = "Priority";
- public static final String TAG_Realm = "Realm";
- public static final String TAG_RequiredProtoPortTuple = "RequiredProtoPortTuple";
- public static final String TAG_Restriction = "Restriction";
- public static final String TAG_RoamingConsortiumOI = "RoamingConsortiumOI";
- public static final String TAG_SIM = "SIM";
- public static final String TAG_SoftTokenApp = "SoftTokenApp";
- public static final String TAG_SPExclusionList = "SPExclusionList";
- public static final String TAG_SSID = "SSID";
- public static final String TAG_StartDate = "StartDate";
- public static final String TAG_SubscriptionParameters = "SubscriptionParameters";
- public static final String TAG_SubscriptionUpdate = "SubscriptionUpdate";
- public static final String TAG_TimeLimit = "TimeLimit";
- public static final String TAG_TrustRoot = "TrustRoot";
- public static final String TAG_TypeOfSubscription = "TypeOfSubscription";
- public static final String TAG_ULBandwidth = "ULBandwidth";
- public static final String TAG_UpdateIdentifier = "UpdateIdentifier";
- public static final String TAG_UpdateInterval = "UpdateInterval";
- public static final String TAG_UpdateMethod = "UpdateMethod";
- public static final String TAG_URI = "URI";
- public static final String TAG_UsageLimits = "UsageLimits";
- public static final String TAG_UsageTimePeriod = "UsageTimePeriod";
- public static final String TAG_Username = "Username";
- public static final String TAG_UsernamePassword = "UsernamePassword";
- public static final String TAG_VendorId = "VendorId";
- public static final String TAG_VendorType = "VendorType";
-
- public static final long IntervalFactor = 60000L; // All MO intervals are in minutes
-
- private static final DateFormat DTFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
-
- private static final Map<String, Map<String, Object>> sSelectionMap;
-
- static {
- DTFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
-
- sSelectionMap = new HashMap<>();
-
- setSelections(TAG_FQDN_Match,
- "exactmatch", Boolean.FALSE,
- "includesubdomains", Boolean.TRUE);
- setSelections(TAG_UpdateMethod,
- "oma-dm-clientinitiated", Boolean.FALSE,
- "spp-clientinitiated", Boolean.TRUE);
- setSelections(TAG_Restriction,
- "homesp", UpdateInfo.UpdateRestriction.HomeSP,
- "roamingpartner", UpdateInfo.UpdateRestriction.RoamingPartner,
- "unrestricted", UpdateInfo.UpdateRestriction.Unrestricted);
- }
-
- private static void setSelections(String key, Object... pairs) {
- Map<String, Object> kvp = new HashMap<>();
- sSelectionMap.put(key, kvp);
- for (int n = 0; n < pairs.length; n += 2) {
- kvp.put(pairs[n].toString(), pairs[n + 1]);
- }
- }
-
- private final File mPpsFile;
- private final boolean mEnabled;
- private final Map<String, HomeSP> mSPs;
-
- public PasspointManagementObjectManager(File ppsFile, boolean hs2enabled) {
- mPpsFile = ppsFile;
- mEnabled = hs2enabled;
- mSPs = new HashMap<>();
- }
-
- public boolean isEnabled() {
- return mEnabled;
- }
-
- public boolean isConfigured() {
- return mEnabled && !mSPs.isEmpty();
- }
-
- public Map<String, HomeSP> getLoadedSPs() {
- return Collections.unmodifiableMap(mSPs);
- }
-
- public List<HomeSP> loadAllSPs() throws IOException {
-
- if (!mEnabled || !mPpsFile.exists()) {
- return Collections.emptyList();
- }
-
- try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(mPpsFile))) {
- mSPs.clear();
- MOTree moTree;
- try {
- moTree = MOTree.unmarshal(in);
- } catch (FileNotFoundException fnfe) {
- return Collections.emptyList(); // Empty file
- }
-
- List<HomeSP> sps = buildSPs(moTree);
- if (sps != null) {
- for (HomeSP sp : sps) {
- if (mSPs.put(sp.getFQDN(), sp) != null) {
- throw new OMAException("Multiple SPs for FQDN '" + sp.getFQDN() + "'");
- } else {
- Log.d(Utils.hs2LogTag(getClass()),
- "retrieved " + sp.getFQDN() + " from PPS");
- }
- }
- return sps;
-
- } else {
- throw new OMAException("Failed to build HomeSP");
- }
- }
- }
-
- public static HomeSP buildSP(String xml) throws IOException, SAXException {
- OMAParser omaParser = new OMAParser();
- MOTree tree = omaParser.parse(xml, OMAConstants.PPS_URN);
- List<HomeSP> spList = buildSPs(tree);
- if (spList.size() != 1) {
- throw new OMAException("Expected exactly one HomeSP, got " + spList.size());
- }
- return spList.iterator().next();
- }
-
- public HomeSP addSP(String xml) throws IOException, SAXException {
- OMAParser omaParser = new OMAParser();
- return addSP(omaParser.parse(xml, OMAConstants.PPS_URN));
- }
-
- private static final List<String> FQDNPath = Arrays.asList(TAG_HomeSP, TAG_FQDN);
-
- /**
- * R1 *only* addSP method.
- *
- * @param homeSP
- * @throws IOException
- */
- public void addSP(HomeSP homeSP) throws IOException {
- if (!mEnabled) {
- throw new IOException("HS2.0 not enabled on this device");
- }
- if (mSPs.containsKey(homeSP.getFQDN())) {
- Log.d(Utils.hs2LogTag(getClass()), "HS20 profile for "
- + homeSP.getFQDN() + " already exists");
- return;
- }
- Log.d(Utils.hs2LogTag(getClass()), "Adding new HS20 profile for " + homeSP.getFQDN());
-
- OMAConstructed dummyRoot = new OMAConstructed(null, TAG_PerProviderSubscription, null);
- buildHomeSPTree(homeSP, dummyRoot, mSPs.size() + 1);
- try {
- addSP(dummyRoot);
- } catch (FileNotFoundException fnfe) {
- MOTree tree = MOTree.buildMgmtTree(OMAConstants.PPS_URN,
- OMAConstants.OMAVersion, dummyRoot);
- writeMO(tree, mPpsFile);
- }
- mSPs.put(homeSP.getFQDN(), homeSP);
- }
-
- public HomeSP addSP(MOTree instanceTree) throws IOException {
- List<HomeSP> spList = buildSPs(instanceTree);
- if (spList.size() != 1) {
- throw new OMAException("Expected exactly one HomeSP, got " + spList.size());
- }
-
- HomeSP sp = spList.iterator().next();
- String fqdn = sp.getFQDN();
- if (mSPs.put(fqdn, sp) != null) {
- throw new OMAException("SP " + fqdn + " already exists");
- }
-
- OMAConstructed pps = (OMAConstructed) instanceTree.getRoot()
- .getChild(TAG_PerProviderSubscription);
-
- try {
- addSP(pps);
- } catch (FileNotFoundException fnfe) {
- MOTree tree = new MOTree(instanceTree.getUrn(), instanceTree.getDtdRev(),
- instanceTree.getRoot());
- writeMO(tree, mPpsFile);
- }
-
- return sp;
- }
-
- /**
- * Add an SP sub-tree. mo must be PPS with an immediate instance child (e.g. Cred01) and an
- * optional UpdateIdentifier,
- *
- * @param mo The new MO
- * @throws IOException
- */
- private void addSP(OMANode mo) throws IOException {
- MOTree moTree;
- try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(mPpsFile))) {
- moTree = MOTree.unmarshal(in);
- moTree.getRoot().addChild(mo);
- }
- writeMO(moTree, mPpsFile);
- }
-
- private static OMAConstructed findTargetTree(MOTree moTree, String fqdn) throws OMAException {
- OMANode pps = moTree.getRoot();
- for (OMANode node : pps.getChildren()) {
- OMANode instance = null;
- if (node.getName().equals(TAG_PerProviderSubscription)) {
- instance = getInstanceNode((OMAConstructed) node);
- } else if (!node.isLeaf()) {
- instance = node;
- }
- if (instance != null) {
- String nodeFqdn = getString(instance.getListValue(FQDNPath.iterator()));
- if (fqdn.equalsIgnoreCase(nodeFqdn)) {
- return (OMAConstructed) node;
- // targetTree is rooted at the PPS
- }
- }
- }
- return null;
- }
-
- private static OMAConstructed getInstanceNode(OMAConstructed root) throws OMAException {
- for (OMANode child : root.getChildren()) {
- if (!child.isLeaf()) {
- return (OMAConstructed) child;
- }
- }
- throw new OMAException("Cannot find instance node");
- }
-
- public int modifySP(String fqdn, Collection<PasspointManagementObjectDefinition> mods)
- throws IOException, SAXException {
-
- Log.d(Utils.hs2LogTag(getClass()), "modifying SP: " + mods);
- MOTree moTree;
- int ppsMods = 0;
- int updateIdentifier;
- try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(mPpsFile))) {
- moTree = MOTree.unmarshal(in);
- // moTree is PPS/?/provider-data
-
- OMAConstructed targetTree = findTargetTree(moTree, fqdn);
- if (targetTree == null) {
- throw new IOException("Failed to find PPS tree for " + fqdn);
- }
- OMAConstructed instance = getInstanceNode(targetTree);
-
- for (PasspointManagementObjectDefinition mod : mods) {
- LinkedList<String> tailPath = getTailPath(mod.getBaseUri(),
- TAG_PerProviderSubscription);
- OMAConstructed modRoot = buildMoTree(mod).getRoot();
- // modRoot is the MgmtTree with the actual object as a
- // direct child (e.g. Credential)
-
- if (tailPath.getFirst().equals(TAG_UpdateIdentifier)) {
- updateIdentifier = getInteger(modRoot.getChildren().iterator().next());
- OMANode oldUdi = targetTree.getChild(TAG_UpdateIdentifier);
- if (getInteger(oldUdi) != updateIdentifier) {
- ppsMods++;
- }
- if (oldUdi != null) {
- targetTree.replaceNode(oldUdi, modRoot.getChild(TAG_UpdateIdentifier));
- } else {
- targetTree.addChild(modRoot.getChild(TAG_UpdateIdentifier));
- }
- } else {
- tailPath.removeFirst(); // Drop the instance
- OMANode current = instance.getListValue(tailPath.iterator());
- if (current == null) {
- throw new IOException("No previous node for " + tailPath + " in " + fqdn);
- }
- for (OMANode newNode : modRoot.getChildren()) {
- // newNode is something like Credential
- // current is the same existing node
- current.getParent().replaceNode(current, newNode);
- ppsMods++;
- }
- }
- }
- }
- writeMO(moTree, mPpsFile);
-
- return ppsMods;
- }
-
- private static MOTree buildMoTree(PasspointManagementObjectDefinition
- managementObjectDefinition)
- throws IOException, SAXException {
-
- OMAParser omaParser = new OMAParser();
- return omaParser.parse(managementObjectDefinition.getMoTree(), OMAConstants.PPS_URN);
- }
-
- private static LinkedList<String> getTailPath(String pathString, String rootName)
- throws IOException {
- String[] path = pathString.split("/");
- int pathIndex;
- for (pathIndex = 0; pathIndex < path.length; pathIndex++) {
- if (path[pathIndex].equalsIgnoreCase(rootName)) {
- pathIndex++;
- break;
- }
- }
- if (pathIndex >= path.length) {
- throw new IOException("Bad node-path: " + pathString);
- }
- LinkedList<String> tailPath = new LinkedList<>();
- while (pathIndex < path.length) {
- tailPath.add(path[pathIndex]);
- pathIndex++;
- }
- return tailPath;
- }
-
- public HomeSP getHomeSP(String fqdn) {
- return mSPs.get(fqdn);
- }
-
- public void removeSP(String fqdn) throws IOException {
- if (mSPs.remove(fqdn) == null) {
- Log.d(Utils.hs2LogTag(getClass()), "No HS20 profile to delete for " + fqdn);
- return;
- }
-
- Log.d(Utils.hs2LogTag(getClass()), "Deleting HS20 profile for " + fqdn);
-
- MOTree moTree;
- try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(mPpsFile))) {
- moTree = MOTree.unmarshal(in);
- OMAConstructed tbd = findTargetTree(moTree, fqdn);
- if (tbd == null) {
- throw new IOException("Node " + fqdn + " doesn't exist in MO tree");
- }
- OMAConstructed pps = moTree.getRoot();
- OMANode removed = pps.removeNode("?", tbd);
- if (removed == null) {
- throw new IOException("Failed to remove " + fqdn + " out of MO tree");
- }
- }
- writeMO(moTree, mPpsFile);
- }
-
- public String getMOTree(String fqdn) throws IOException {
- if (fqdn == null) {
- return null;
- }
- try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(mPpsFile))) {
- MOTree moTree = MOTree.unmarshal(in);
- OMAConstructed target = findTargetTree(moTree, fqdn);
- if (target == null) {
- return null;
- }
- return MOTree.buildMgmtTree(OMAConstants.PPS_URN,
- OMAConstants.OMAVersion, target).toXml();
- } catch (FileNotFoundException fnfe) {
- return null;
- }
- }
-
- private static void writeMO(MOTree moTree, File f) throws IOException {
- try (BufferedOutputStream out =
- new BufferedOutputStream(new FileOutputStream(f, false))) {
- moTree.marshal(out);
- out.flush();
- }
- }
-
- private static OMANode buildHomeSPTree(HomeSP homeSP, OMAConstructed root, int instanceID)
- throws IOException {
- OMANode providerSubNode = root.addChild(getInstanceString(instanceID),
- null, null, null);
-
- // The HomeSP:
- OMANode homeSpNode = providerSubNode.addChild(TAG_HomeSP, null, null, null);
- if (!homeSP.getSSIDs().isEmpty()) {
- OMAConstructed nwkIDNode =
- (OMAConstructed) homeSpNode.addChild(TAG_NetworkID, null, null, null);
- int instance = 0;
- for (Map.Entry<String, Long> entry : homeSP.getSSIDs().entrySet()) {
- OMAConstructed inode =
- (OMAConstructed) nwkIDNode.addChild(getInstanceString(instance++),
- null, null, null);
- inode.addChild(TAG_SSID, null, entry.getKey(), null);
- if (entry.getValue() != null) {
- inode.addChild(TAG_HESSID, null,
- String.format("%012x", entry.getValue()), null);
- }
- }
- }
-
- homeSpNode.addChild(TAG_FriendlyName, null, homeSP.getFriendlyName(), null);
-
- if (homeSP.getIconURL() != null) {
- homeSpNode.addChild(TAG_IconURL, null, homeSP.getIconURL(), null);
- }
-
- homeSpNode.addChild(TAG_FQDN, null, homeSP.getFQDN(), null);
-
- if (!homeSP.getMatchAllOIs().isEmpty() || !homeSP.getMatchAnyOIs().isEmpty()) {
- OMAConstructed homeOIList =
- (OMAConstructed) homeSpNode.addChild(TAG_HomeOIList, null, null, null);
-
- int instance = 0;
- for (Long oi : homeSP.getMatchAllOIs()) {
- OMAConstructed inode =
- (OMAConstructed) homeOIList.addChild(getInstanceString(instance++),
- null, null, null);
- inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null);
- inode.addChild(TAG_HomeOIRequired, null, "TRUE", null);
- }
- for (Long oi : homeSP.getMatchAnyOIs()) {
- OMAConstructed inode =
- (OMAConstructed) homeOIList.addChild(getInstanceString(instance++),
- null, null, null);
- inode.addChild(TAG_HomeOI, null, String.format("%x", oi), null);
- inode.addChild(TAG_HomeOIRequired, null, "FALSE", null);
- }
- }
-
- if (!homeSP.getOtherHomePartners().isEmpty()) {
- OMAConstructed otherPartners =
- (OMAConstructed) homeSpNode.addChild(TAG_OtherHomePartners, null, null, null);
- int instance = 0;
- for (String fqdn : homeSP.getOtherHomePartners()) {
- OMAConstructed inode =
- (OMAConstructed) otherPartners.addChild(getInstanceString(instance++),
- null, null, null);
- inode.addChild(TAG_FQDN, null, fqdn, null);
- }
- }
-
- if (!homeSP.getRoamingConsortiums().isEmpty()) {
- homeSpNode.addChild(TAG_RoamingConsortiumOI, null,
- getRCList(homeSP.getRoamingConsortiums()), null);
- }
-
- // The Credential:
- OMANode credentialNode = providerSubNode.addChild(TAG_Credential, null, null, null);
- Credential cred = homeSP.getCredential();
- EAPMethod method = cred.getEAPMethod();
-
- if (cred.getCtime() > 0) {
- credentialNode.addChild(TAG_CreationDate,
- null, DTFormat.format(new Date(cred.getCtime())), null);
- }
- if (cred.getExpTime() > 0) {
- credentialNode.addChild(TAG_ExpirationDate,
- null, DTFormat.format(new Date(cred.getExpTime())), null);
- }
-
- if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_SIM
- || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKA
- || method.getEAPMethodID() == EAP.EAPMethodID.EAP_AKAPrim) {
-
- OMANode simNode = credentialNode.addChild(TAG_SIM, null, null, null);
- simNode.addChild(TAG_IMSI, null, cred.getImsi().toString(), null);
- simNode.addChild(TAG_EAPType, null,
- Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null);
-
- } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TTLS) {
-
- OMANode unpNode = credentialNode.addChild(TAG_UsernamePassword, null, null, null);
- unpNode.addChild(TAG_Username, null, cred.getUserName(), null);
- unpNode.addChild(TAG_Password, null,
- Base64.encodeToString(cred.getPassword().getBytes(StandardCharsets.UTF_8),
- Base64.DEFAULT), null);
- OMANode eapNode = unpNode.addChild(TAG_EAPMethod, null, null, null);
- eapNode.addChild(TAG_EAPType, null,
- Integer.toString(EAP.mapEAPMethod(method.getEAPMethodID())), null);
- eapNode.addChild(TAG_InnerMethod, null,
- ((NonEAPInnerAuth) method.getAuthParam()).getOMAtype(), null);
-
- } else if (method.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS) {
-
- OMANode certNode = credentialNode.addChild(TAG_DigitalCertificate, null, null, null);
- certNode.addChild(TAG_CertificateType, null, Credential.CertTypeX509, null);
- certNode.addChild(TAG_CertSHA256Fingerprint, null,
- Utils.toHex(cred.getFingerPrint()), null);
-
- } else {
- throw new OMAException("Invalid credential on " + homeSP.getFQDN());
- }
-
- credentialNode.addChild(TAG_Realm, null, cred.getRealm(), null);
-
- // !!! Note: This node defines CRL checking through OSCP, I suspect we won't be able
- // to do that so it is commented out:
- //credentialNode.addChild(TAG_CheckAAAServerCertStatus, null, "TRUE", null);
- return providerSubNode;
- }
-
- private static String getInstanceString(int instance) {
- return "r1i" + instance;
- }
-
- private static String getRCList(Collection<Long> rcs) {
- StringBuilder builder = new StringBuilder();
- boolean first = true;
- for (Long roamingConsortium : rcs) {
- if (first) {
- first = false;
- } else {
- builder.append(',');
- }
- builder.append(String.format("%x", roamingConsortium));
- }
- return builder.toString();
- }
-
- private static List<HomeSP> buildSPs(MOTree moTree) throws OMAException {
- OMAConstructed spList;
- List<HomeSP> homeSPs = new ArrayList<>();
- if (moTree.getRoot().getName().equals(TAG_PerProviderSubscription)) {
- // The old PPS file was rooted at PPS instead of MgmtTree to conserve space
- spList = moTree.getRoot();
-
- if (spList == null) {
- return homeSPs;
- }
-
- for (OMANode node : spList.getChildren()) {
- if (!node.isLeaf()) {
- homeSPs.add(buildHomeSP(node, 0));
- }
- }
- } else {
- for (OMANode ppsRoot : moTree.getRoot().getChildren()) {
- if (ppsRoot.getName().equals(TAG_PerProviderSubscription)) {
- Integer updateIdentifier = null;
- OMANode instance = null;
- for (OMANode child : ppsRoot.getChildren()) {
- if (child.getName().equals(TAG_UpdateIdentifier)) {
- updateIdentifier = getInteger(child);
- } else if (!child.isLeaf()) {
- instance = child;
- }
- }
- if (instance == null) {
- throw new OMAException("PPS node missing instance node");
- }
- homeSPs.add(buildHomeSP(instance,
- updateIdentifier != null ? updateIdentifier : 0));
- }
- }
- }
-
- return homeSPs;
- }
-
- private static HomeSP buildHomeSP(OMANode ppsRoot, int updateIdentifier) throws OMAException {
- OMANode spRoot = ppsRoot.getChild(TAG_HomeSP);
-
- String fqdn = spRoot.getScalarValue(Arrays.asList(TAG_FQDN).iterator());
- String friendlyName = spRoot.getScalarValue(Arrays.asList(TAG_FriendlyName).iterator());
- String iconURL = spRoot.getScalarValue(Arrays.asList(TAG_IconURL).iterator());
-
- HashSet<Long> roamingConsortiums = new HashSet<>();
- String oiString = spRoot.getScalarValue(Arrays.asList(TAG_RoamingConsortiumOI).iterator());
- if (oiString != null) {
- for (String oi : oiString.split(",")) {
- roamingConsortiums.add(Long.parseLong(oi.trim(), 16));
- }
- }
-
- Map<String, Long> ssids = new HashMap<>();
-
- OMANode ssidListNode = spRoot.getListValue(Arrays.asList(TAG_NetworkID).iterator());
- if (ssidListNode != null) {
- for (OMANode ssidRoot : ssidListNode.getChildren()) {
- OMANode hessidNode = ssidRoot.getChild(TAG_HESSID);
- ssids.put(ssidRoot.getChild(TAG_SSID).getValue(), getMac(hessidNode));
- }
- }
-
- Set<Long> matchAnyOIs = new HashSet<>();
- List<Long> matchAllOIs = new ArrayList<>();
- OMANode homeOIListNode = spRoot.getListValue(Arrays.asList(TAG_HomeOIList).iterator());
- if (homeOIListNode != null) {
- for (OMANode homeOIRoot : homeOIListNode.getChildren()) {
- String homeOI = homeOIRoot.getChild(TAG_HomeOI).getValue();
- if (Boolean.parseBoolean(homeOIRoot.getChild(TAG_HomeOIRequired).getValue())) {
- matchAllOIs.add(Long.parseLong(homeOI, 16));
- } else {
- matchAnyOIs.add(Long.parseLong(homeOI, 16));
- }
- }
- }
-
- Set<String> otherHomePartners = new HashSet<>();
- OMANode otherListNode =
- spRoot.getListValue(Arrays.asList(TAG_OtherHomePartners).iterator());
- if (otherListNode != null) {
- for (OMANode fqdnNode : otherListNode.getChildren()) {
- otherHomePartners.add(fqdnNode.getChild(TAG_FQDN).getValue());
- }
- }
-
- Credential credential = buildCredential(ppsRoot.getChild(TAG_Credential));
-
- OMANode policyNode = ppsRoot.getChild(TAG_Policy);
- Policy policy = policyNode != null ? new Policy(policyNode) : null;
-
- Map<String, String> aaaTrustRoots;
- OMANode aaaRootNode = ppsRoot.getChild(TAG_AAAServerTrustRoot);
- if (aaaRootNode == null) {
- aaaTrustRoots = null;
- } else {
- aaaTrustRoots = new HashMap<>(aaaRootNode.getChildren().size());
- for (OMANode child : aaaRootNode.getChildren()) {
- aaaTrustRoots.put(getString(child, TAG_CertURL),
- getString(child, TAG_CertSHA256Fingerprint));
- }
- }
-
- OMANode updateNode = ppsRoot.getChild(TAG_SubscriptionUpdate);
- UpdateInfo subscriptionUpdate = updateNode != null ? new UpdateInfo(updateNode) : null;
- OMANode subNode = ppsRoot.getChild(TAG_SubscriptionParameters);
- SubscriptionParameters subscriptionParameters = subNode != null
- ? new SubscriptionParameters(subNode) : null;
-
- return new HomeSP(ssids, fqdn, roamingConsortiums, otherHomePartners,
- matchAnyOIs, matchAllOIs, friendlyName, iconURL, credential,
- policy, getInteger(ppsRoot.getChild(TAG_CredentialPriority), 0),
- aaaTrustRoots, subscriptionUpdate, subscriptionParameters, updateIdentifier);
- }
-
- private static Credential buildCredential(OMANode credNode) throws OMAException {
- long ctime = getTime(credNode.getChild(TAG_CreationDate));
- long expTime = getTime(credNode.getChild(TAG_ExpirationDate));
- String realm = getString(credNode.getChild(TAG_Realm));
- boolean checkAAACert = getBoolean(credNode.getChild(TAG_CheckAAAServerCertStatus));
-
- OMANode unNode = credNode.getChild(TAG_UsernamePassword);
- OMANode certNode = credNode.getChild(TAG_DigitalCertificate);
- OMANode simNode = credNode.getChild(TAG_SIM);
-
- int alternatives = 0;
- alternatives += unNode != null ? 1 : 0;
- alternatives += certNode != null ? 1 : 0;
- alternatives += simNode != null ? 1 : 0;
- if (alternatives != 1) {
- throw new OMAException("Expected exactly one credential type, got " + alternatives);
- }
-
- if (unNode != null) {
- String userName = getString(unNode.getChild(TAG_Username));
- String password = getString(unNode.getChild(TAG_Password));
- boolean machineManaged = getBoolean(unNode.getChild(TAG_MachineManaged));
- String softTokenApp = getString(unNode.getChild(TAG_SoftTokenApp));
- boolean ableToShare = getBoolean(unNode.getChild(TAG_AbleToShare));
-
- OMANode eapMethodNode = unNode.getChild(TAG_EAPMethod);
- int eapID = getInteger(eapMethodNode.getChild(TAG_EAPType));
-
- EAP.EAPMethodID eapMethodID = EAP.mapEAPMethod(eapID);
- if (eapMethodID == null) {
- throw new OMAException("Unknown EAP method: " + eapID);
- }
-
- Long vid = getOptionalInteger(eapMethodNode.getChild(TAG_VendorId));
- Long vtype = getOptionalInteger(eapMethodNode.getChild(TAG_VendorType));
- Long innerEAPType = getOptionalInteger(eapMethodNode.getChild(TAG_InnerEAPType));
- EAP.EAPMethodID innerEAPMethod = null;
- if (innerEAPType != null) {
- innerEAPMethod = EAP.mapEAPMethod(innerEAPType.intValue());
- if (innerEAPMethod == null) {
- throw new OMAException("Bad inner EAP method: " + innerEAPType);
- }
- }
-
- Long innerVid = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorID));
- Long innerVtype = getOptionalInteger(eapMethodNode.getChild(TAG_InnerVendorType));
- String innerNonEAPMethod = getString(eapMethodNode.getChild(TAG_InnerMethod));
-
- EAPMethod eapMethod;
- if (innerEAPMethod != null) {
- eapMethod = new EAPMethod(eapMethodID, new InnerAuthEAP(innerEAPMethod));
- } else if (vid != null) {
- eapMethod = new EAPMethod(eapMethodID,
- new ExpandedEAPMethod(EAP.AuthInfoID.ExpandedEAPMethod,
- vid.intValue(), vtype));
- } else if (innerVid != null) {
- eapMethod =
- new EAPMethod(eapMethodID, new ExpandedEAPMethod(EAP.AuthInfoID
- .ExpandedInnerEAPMethod, innerVid.intValue(), innerVtype));
- } else if (innerNonEAPMethod != null) {
- eapMethod = new EAPMethod(eapMethodID, new NonEAPInnerAuth(innerNonEAPMethod));
- } else {
- throw new OMAException("Incomplete set of EAP parameters");
- }
-
- return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, userName,
- password, machineManaged, softTokenApp, ableToShare);
- }
- if (certNode != null) {
- try {
- String certTypeString = getString(certNode.getChild(TAG_CertificateType));
- byte[] fingerPrint = getOctets(certNode.getChild(TAG_CertSHA256Fingerprint));
-
- EAPMethod eapMethod = new EAPMethod(EAP.EAPMethodID.EAP_TLS, null);
-
- return new Credential(ctime, expTime, realm, checkAAACert, eapMethod,
- Credential.mapCertType(certTypeString), fingerPrint);
- } catch (NumberFormatException nfe) {
- throw new OMAException("Bad hex string: " + nfe.toString());
- }
- }
- if (simNode != null) {
- try {
- IMSIParameter imsi = new IMSIParameter(getString(simNode.getChild(TAG_IMSI)));
-
- EAPMethod eapMethod =
- new EAPMethod(EAP.mapEAPMethod(getInteger(simNode.getChild(TAG_EAPType))),
- null);
-
- return new Credential(ctime, expTime, realm, checkAAACert, eapMethod, imsi);
- } catch (IOException ioe) {
- throw new OMAException("Failed to parse IMSI: " + ioe);
- }
- }
- throw new OMAException("Missing credential parameters");
- }
-
- public static OMANode getChild(OMANode node, String key) throws OMAException {
- OMANode child = node.getChild(key);
- if (child == null) {
- throw new OMAException("No such node: " + key);
- }
- return child;
- }
-
- public static String getString(OMANode node, String key) throws OMAException {
- OMANode child = node.getChild(key);
- if (child == null) {
- throw new OMAException("Missing value for " + key);
- } else if (!child.isLeaf()) {
- throw new OMAException(key + " is not a leaf node");
- }
- return child.getValue();
- }
-
- public static long getLong(OMANode node, String key, Long dflt) throws OMAException {
- OMANode child = node.getChild(key);
- if (child == null) {
- if (dflt != null) {
- return dflt;
- } else {
- throw new OMAException("Missing value for " + key);
- }
- } else {
- if (!child.isLeaf()) {
- throw new OMAException(key + " is not a leaf node");
- }
- String value = child.getValue();
- try {
- long result = Long.parseLong(value);
- if (result < 0) {
- throw new OMAException("Negative value for " + key);
- }
- return result;
- } catch (NumberFormatException nfe) {
- throw new OMAException("Value for " + key + " is non-numeric: " + value);
- }
- }
- }
-
- public static <T> T getSelection(OMANode node, String key) throws OMAException {
- OMANode child = node.getChild(key);
- if (child == null) {
- throw new OMAException("Missing value for " + key);
- } else if (!child.isLeaf()) {
- throw new OMAException(key + " is not a leaf node");
- }
- return getSelection(key, child.getValue());
- }
-
- public static <T> T getSelection(String key, String value) throws OMAException {
- if (value == null) {
- throw new OMAException("No value for " + key);
- }
- Map<String, Object> kvp = sSelectionMap.get(key);
- T result = (T) kvp.get(value.toLowerCase());
- if (result == null) {
- throw new OMAException("Invalid value '" + value + "' for " + key);
- }
- return result;
- }
-
- private static boolean getBoolean(OMANode boolNode) {
- return boolNode != null && Boolean.parseBoolean(boolNode.getValue());
- }
-
- public static String getString(OMANode stringNode) {
- return stringNode != null ? stringNode.getValue() : null;
- }
-
- private static int getInteger(OMANode intNode, int dflt) throws OMAException {
- if (intNode == null) {
- return dflt;
- }
- return getInteger(intNode);
- }
-
- private static int getInteger(OMANode intNode) throws OMAException {
- if (intNode == null) {
- throw new OMAException("Missing integer value");
- }
- try {
- return Integer.parseInt(intNode.getValue());
- } catch (NumberFormatException nfe) {
- throw new OMAException("Invalid integer: " + intNode.getValue());
- }
- }
-
- private static Long getMac(OMANode macNode) throws OMAException {
- if (macNode == null) {
- return null;
- }
- try {
- return Long.parseLong(macNode.getValue(), 16);
- } catch (NumberFormatException nfe) {
- throw new OMAException("Invalid MAC: " + macNode.getValue());
- }
- }
-
- private static Long getOptionalInteger(OMANode intNode) throws OMAException {
- if (intNode == null) {
- return null;
- }
- try {
- return Long.parseLong(intNode.getValue());
- } catch (NumberFormatException nfe) {
- throw new OMAException("Invalid integer: " + intNode.getValue());
- }
- }
-
- public static long getTime(OMANode timeNode) throws OMAException {
- if (timeNode == null) {
- return Utils.UNSET_TIME;
- }
- String timeText = timeNode.getValue();
- try {
- Date date = DTFormat.parse(timeText);
- return date.getTime();
- } catch (ParseException pe) {
- throw new OMAException("Badly formatted time: " + timeText);
- }
- }
-
- private static byte[] getOctets(OMANode octetNode) throws OMAException {
- if (octetNode == null) {
- throw new OMAException("Missing byte value");
- }
- return Utils.hexToBytes(octetNode.getValue());
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/omadm/XMLNode.java b/service/java/com/android/server/wifi/hotspot2/omadm/XMLNode.java
deleted file mode 100644
index 76b7076..0000000
--- a/service/java/com/android/server/wifi/hotspot2/omadm/XMLNode.java
+++ /dev/null
@@ -1,287 +0,0 @@
-package com.android.server.wifi.hotspot2.omadm;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-public class XMLNode {
- private final String mTag;
- private final Map<String, NodeAttribute> mAttributes;
- private final List<XMLNode> mChildren;
- private final XMLNode mParent;
- private MOTree mMO;
- private StringBuilder mTextBuilder;
- private String mText;
-
- private static final String XML_SPECIAL_CHARS = "\"'<>&";
- private static final Set<Character> XML_SPECIAL = new HashSet<>();
- private static final String CDATA_OPEN = "<![CDATA[";
- private static final String CDATA_CLOSE = "]]>";
-
- static {
- for (int n = 0; n < XML_SPECIAL_CHARS.length(); n++) {
- XML_SPECIAL.add(XML_SPECIAL_CHARS.charAt(n));
- }
- }
-
- public XMLNode(XMLNode parent, String tag, Attributes attributes) throws SAXException {
- mTag = tag;
-
- mAttributes = new HashMap<>();
-
- if (attributes.getLength() > 0) {
- for (int n = 0; n < attributes.getLength(); n++)
- mAttributes.put(attributes.getQName(n), new NodeAttribute(attributes.getQName(n),
- attributes.getType(n), attributes.getValue(n)));
- }
-
- mParent = parent;
- mChildren = new ArrayList<>();
-
- mTextBuilder = new StringBuilder();
- }
-
- public XMLNode(XMLNode parent, String tag, Map<String, String> attributes) {
- mTag = tag;
-
- mAttributes = new HashMap<>(attributes == null ? 0 : attributes.size());
-
- if (attributes != null) {
- for (Map.Entry<String, String> entry : attributes.entrySet()) {
- mAttributes.put(entry.getKey(), new NodeAttribute(entry.getKey(), "", entry.getValue()));
- }
- }
-
- mParent = parent;
- mChildren = new ArrayList<>();
-
- mTextBuilder = new StringBuilder();
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (thatObject == this) {
- return true;
- } else if (thatObject.getClass() != XMLNode.class) {
- return false;
- }
-
- XMLNode that = (XMLNode) thatObject;
- if (!getTag().equals(that.getTag())
- || mAttributes.size() != that.mAttributes.size()
- || mChildren.size() != that.mChildren.size()) {
- return false;
- }
-
- for (Map.Entry<String, NodeAttribute> entry : mAttributes.entrySet()) {
- if (!entry.getValue().equals(that.mAttributes.get(entry.getKey()))) {
- return false;
- }
- }
-
- List<XMLNode> cloneOfThat = new ArrayList<>(that.mChildren);
- for (XMLNode child : mChildren) {
- Iterator<XMLNode> thatChildren = cloneOfThat.iterator();
- boolean found = false;
- while (thatChildren.hasNext()) {
- XMLNode thatChild = thatChildren.next();
- if (child.equals(thatChild)) {
- found = true;
- thatChildren.remove();
- break;
- }
- }
- if (!found) {
- return false;
- }
- }
- return true;
- }
-
- public void setText(String text) {
- mText = text;
- mTextBuilder = null;
- }
-
- public void addText(char[] chs, int start, int length) {
- String s = new String(chs, start, length);
- String trimmed = s.trim();
- if (trimmed.isEmpty())
- return;
-
- if (s.charAt(0) != trimmed.charAt(0))
- mTextBuilder.append(' ');
- mTextBuilder.append(trimmed);
- if (s.charAt(s.length() - 1) != trimmed.charAt(trimmed.length() - 1))
- mTextBuilder.append(' ');
- }
-
- public void addChild(XMLNode child) {
- mChildren.add(child);
- }
-
- public void close() throws IOException, SAXException {
- String text = mTextBuilder.toString().trim();
- StringBuilder filtered = new StringBuilder(text.length());
- for (int n = 0; n < text.length(); n++) {
- char ch = text.charAt(n);
- if (ch >= ' ')
- filtered.append(ch);
- }
-
- mText = filtered.toString();
- mTextBuilder = null;
-
- if (MOTree.hasMgmtTreeTag(mText)) {
- try {
- NodeAttribute urn = mAttributes.get(OMAConstants.SppMOAttribute);
- OMAParser omaParser = new OMAParser();
- mMO = omaParser.parse(mText, urn != null ? urn.getValue() : null);
- }
- catch (SAXException | IOException e) {
- mMO = null;
- }
- }
- }
-
- public String getTag() {
- return mTag;
- }
-
- public String getNameSpace() throws OMAException {
- String[] nsn = mTag.split(":");
- if (nsn.length != 2) {
- throw new OMAException("Non-namespaced tag: '" + mTag + "'");
- }
- return nsn[0];
- }
-
- public String getStrippedTag() throws OMAException {
- String[] nsn = mTag.split(":");
- if (nsn.length != 2) {
- throw new OMAException("Non-namespaced tag: '" + mTag + "'");
- }
- return nsn[1].toLowerCase();
- }
-
- public XMLNode getSoleChild() throws OMAException{
- if (mChildren.size() != 1) {
- throw new OMAException("Expected exactly one child to " + mTag);
- }
- return mChildren.get(0);
- }
-
- public XMLNode getParent() {
- return mParent;
- }
-
- public String getText() {
- return mText;
- }
-
- public Map<String, NodeAttribute> getAttributes() {
- return Collections.unmodifiableMap(mAttributes);
- }
-
- /**
- * Get the attributes of this node as a map of attribute name to attribute value.
- * @return The attribute mapping.
- */
- public Map<String, String> getTextualAttributes() {
- Map<String, String> map = new HashMap<>(mAttributes.size());
- for (Map.Entry<String, NodeAttribute> entry : mAttributes.entrySet()) {
- map.put(entry.getKey(), entry.getValue().getValue());
- }
- return map;
- }
-
- public String getAttributeValue(String name) {
- NodeAttribute nodeAttribute = mAttributes.get(name);
- return nodeAttribute != null ? nodeAttribute.getValue() : null;
- }
-
- public List<XMLNode> getChildren() {
- return mChildren;
- }
-
- public MOTree getMOTree() {
- return mMO;
- }
-
- private void toString(char[] indent, StringBuilder sb) {
- Arrays.fill(indent, ' ');
-
- sb.append(indent).append('<').append(mTag);
- for (Map.Entry<String, NodeAttribute> entry : mAttributes.entrySet()) {
- sb.append(' ').append(entry.getKey()).append("='").append(entry.getValue().getValue()).append('\'');
- }
-
- if (mText != null && !mText.isEmpty()) {
- sb.append('>').append(escapeCdata(mText)).append("</").append(mTag).append(">\n");
- }
- else if (mChildren.isEmpty()) {
- sb.append("/>\n");
- }
- else {
- sb.append(">\n");
- char[] subIndent = Arrays.copyOf(indent, indent.length + 2);
- for (XMLNode child : mChildren) {
- child.toString(subIndent, sb);
- }
- sb.append(indent).append("</").append(mTag).append(">\n");
- }
- }
-
- private static String escapeCdata(String text) {
- if (!escapable(text)) {
- return text;
- }
-
- // Any appearance of ]]> in the text must be split into "]]" | "]]>" | <![CDATA[ | ">"
- // i.e. "split the sequence by putting a close CDATA and a new open CDATA before the '>'
- StringBuilder sb = new StringBuilder();
- sb.append(CDATA_OPEN);
- int start = 0;
- for (;;) {
- int etoken = text.indexOf(CDATA_CLOSE);
- if (etoken >= 0) {
- sb.append(text.substring(start, etoken + 2)).append(CDATA_CLOSE).append(CDATA_OPEN);
- start = etoken + 2;
- }
- else {
- if (start < text.length() - 1) {
- sb.append(text.substring(start));
- }
- break;
- }
- }
- sb.append(CDATA_CLOSE);
- return sb.toString();
- }
-
- private static boolean escapable(String s) {
- for (int n = 0; n < s.length(); n++) {
- if (XML_SPECIAL.contains(s.charAt(n))) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- toString(new char[0], sb);
- return sb.toString();
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/pps/Credential.java b/service/java/com/android/server/wifi/hotspot2/pps/Credential.java
deleted file mode 100644
index 4a06021..0000000
--- a/service/java/com/android/server/wifi/hotspot2/pps/Credential.java
+++ /dev/null
@@ -1,357 +0,0 @@
-package com.android.server.wifi.hotspot2.pps;
-
-import android.net.wifi.WifiEnterpriseConfig;
-import android.security.Credentials;
-import android.security.KeyStore;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.Log;
-
-import com.android.server.wifi.IMSIParameter;
-import com.android.server.wifi.anqp.eap.EAP;
-import com.android.server.wifi.anqp.eap.EAPMethod;
-import com.android.server.wifi.anqp.eap.NonEAPInnerAuth;
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.hotspot2.omadm.OMAException;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
-import java.util.Arrays;
-
-public class Credential {
- public enum CertType {IEEE, x509v3}
-
- public static final String CertTypeX509 = "x509v3";
- public static final String CertTypeIEEE = "802.1ar";
-
- private final long mCtime;
- private final long mExpTime;
- private final String mRealm;
- private final boolean mCheckAAACert;
-
- private final String mUserName;
- private final String mPassword;
- private final boolean mDisregardPassword;
- private final boolean mMachineManaged;
- private final String mSTokenApp;
- private final boolean mShare;
- private final EAPMethod mEAPMethod;
-
- private final CertType mCertType;
- private final byte[] mFingerPrint;
-
- private final IMSIParameter mImsi;
-
- public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
- EAPMethod eapMethod, String userName, String password,
- boolean machineManaged, String stApp, boolean share) {
- mCtime = ctime;
- mExpTime = expTime;
- mRealm = realm;
- mCheckAAACert = checkAAACert;
- mEAPMethod = eapMethod;
- mUserName = userName;
-
- if (!TextUtils.isEmpty(password)) {
- byte[] pwOctets = Base64.decode(password, Base64.DEFAULT);
- mPassword = new String(pwOctets, StandardCharsets.UTF_8);
- } else {
- mPassword = null;
- }
- mDisregardPassword = false;
-
- mMachineManaged = machineManaged;
- mSTokenApp = stApp;
- mShare = share;
-
- mCertType = null;
- mFingerPrint = null;
-
- mImsi = null;
- }
-
- public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
- EAPMethod eapMethod, Credential.CertType certType, byte[] fingerPrint) {
- mCtime = ctime;
- mExpTime = expTime;
- mRealm = realm;
- mCheckAAACert = checkAAACert;
- mEAPMethod = eapMethod;
- mCertType = certType;
- mFingerPrint = fingerPrint;
-
- mUserName = null;
- mPassword = null;
- mDisregardPassword = false;
- mMachineManaged = false;
- mSTokenApp = null;
- mShare = false;
-
- mImsi = null;
- }
-
- public Credential(long ctime, long expTime, String realm, boolean checkAAACert,
- EAPMethod eapMethod, IMSIParameter imsi) {
- mCtime = ctime;
- mExpTime = expTime;
- mRealm = realm;
- mCheckAAACert = checkAAACert;
- mEAPMethod = eapMethod;
- mImsi = imsi;
-
- mCertType = null;
- mFingerPrint = null;
-
- mUserName = null;
- mPassword = null;
- mDisregardPassword = false;
- mMachineManaged = false;
- mSTokenApp = null;
- mShare = false;
- }
-
- public Credential(Credential other, String password) {
- mCtime = other.mCtime;
- mExpTime = other.mExpTime;
- mRealm = other.mRealm;
- mCheckAAACert = other.mCheckAAACert;
- mUserName = other.mUserName;
- mPassword = password;
- mDisregardPassword = other.mDisregardPassword;
- mMachineManaged = other.mMachineManaged;
- mSTokenApp = other.mSTokenApp;
- mShare = other.mShare;
- mEAPMethod = other.mEAPMethod;
- mCertType = other.mCertType;
- mFingerPrint = other.mFingerPrint;
- mImsi = other.mImsi;
- }
-
- public Credential(WifiEnterpriseConfig enterpriseConfig, KeyStore keyStore, boolean update)
- throws IOException {
- mCtime = Utils.UNSET_TIME;
- mExpTime = Utils.UNSET_TIME;
- mRealm = enterpriseConfig.getRealm();
- mCheckAAACert = false;
- mEAPMethod = mapEapMethod(enterpriseConfig.getEapMethod(),
- enterpriseConfig.getPhase2Method());
- mCertType = mEAPMethod.getEAPMethodID() == EAP.EAPMethodID.EAP_TLS ? CertType.x509v3 : null;
- byte[] fingerPrint;
-
- if (enterpriseConfig.getClientCertificate() != null) {
- // !!! Not sure this will be true in any practical instances:
- try {
- MessageDigest digester = MessageDigest.getInstance("SHA-256");
- fingerPrint = digester.digest(enterpriseConfig.getClientCertificate().getEncoded());
- } catch (GeneralSecurityException gse) {
- Log.e(Utils.hs2LogTag(getClass()),
- "Failed to generate certificate fingerprint: " + gse);
- fingerPrint = null;
- }
- } else if (enterpriseConfig.getClientCertificateAlias() != null) {
- String alias = enterpriseConfig.getClientCertificateAlias();
- byte[] octets = keyStore.get(Credentials.USER_CERTIFICATE + alias);
- if (octets != null) {
- try {
- MessageDigest digester = MessageDigest.getInstance("SHA-256");
- fingerPrint = digester.digest(octets);
- } catch (GeneralSecurityException gse) {
- Log.e(Utils.hs2LogTag(getClass()), "Failed to construct digest: " + gse);
- fingerPrint = null;
- }
- } else // !!! The current alias is *not* derived from the fingerprint...
- {
- try {
- fingerPrint = Base64.decode(enterpriseConfig.getClientCertificateAlias(),
- Base64.DEFAULT);
- } catch (IllegalArgumentException ie) {
- Log.e(Utils.hs2LogTag(getClass()), "Bad base 64 alias");
- fingerPrint = null;
- }
- }
- } else {
- fingerPrint = null;
- }
- mFingerPrint = fingerPrint;
- String imsi = enterpriseConfig.getPlmn();
- mImsi = imsi == null || imsi.length() == 0 ? null : new IMSIParameter(imsi);
- mUserName = enterpriseConfig.getIdentity();
- mPassword = enterpriseConfig.getPassword();
- mDisregardPassword = update && mPassword.length() < 2;
- mMachineManaged = false;
- mSTokenApp = null;
- mShare = false;
- }
-
- public static CertType mapCertType(String certType) throws OMAException {
- if (certType.equalsIgnoreCase(CertTypeX509)) {
- return CertType.x509v3;
- } else if (certType.equalsIgnoreCase(CertTypeIEEE)) {
- return CertType.IEEE;
- } else {
- throw new OMAException("Invalid cert type: '" + certType + "'");
- }
- }
-
- private static EAPMethod mapEapMethod(int eapMethod, int phase2Method) throws IOException {
- switch (eapMethod) {
- case WifiEnterpriseConfig.Eap.TLS:
- return new EAPMethod(EAP.EAPMethodID.EAP_TLS, null);
- case WifiEnterpriseConfig.Eap.TTLS:
- /* keep this table in sync with WifiEnterpriseConfig.Phase2 enum */
- NonEAPInnerAuth inner;
- switch (phase2Method) {
- case WifiEnterpriseConfig.Phase2.PAP:
- inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.PAP);
- break;
- case WifiEnterpriseConfig.Phase2.MSCHAP:
- inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAP);
- break;
- case WifiEnterpriseConfig.Phase2.MSCHAPV2:
- inner = new NonEAPInnerAuth(NonEAPInnerAuth.NonEAPType.MSCHAPv2);
- break;
- default:
- throw new IOException("TTLS phase2 method " +
- phase2Method + " not valid for Passpoint");
- }
- return new EAPMethod(EAP.EAPMethodID.EAP_TTLS, inner);
- case WifiEnterpriseConfig.Eap.SIM:
- return new EAPMethod(EAP.EAPMethodID.EAP_SIM, null);
- case WifiEnterpriseConfig.Eap.AKA:
- return new EAPMethod(EAP.EAPMethodID.EAP_AKA, null);
- case WifiEnterpriseConfig.Eap.AKA_PRIME:
- return new EAPMethod(EAP.EAPMethodID.EAP_AKAPrim, null);
- default:
- String methodName;
- if (eapMethod >= 0 && eapMethod < WifiEnterpriseConfig.Eap.strings.length) {
- methodName = WifiEnterpriseConfig.Eap.strings[eapMethod];
- } else {
- methodName = Integer.toString(eapMethod);
- }
- throw new IOException("EAP method id " + methodName + " is not valid for Passpoint");
- }
- }
-
- public EAPMethod getEAPMethod() {
- return mEAPMethod;
- }
-
- public String getRealm() {
- return mRealm;
- }
-
- public IMSIParameter getImsi() {
- return mImsi;
- }
-
- public String getUserName() {
- return mUserName;
- }
-
- public String getPassword() {
- return mPassword;
- }
-
- public boolean hasDisregardPassword() {
- return mDisregardPassword;
- }
-
- public CertType getCertType() {
- return mCertType;
- }
-
- public byte[] getFingerPrint() {
- return mFingerPrint;
- }
-
- public long getCtime() {
- return mCtime;
- }
-
- public long getExpTime() {
- return mExpTime;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- Credential that = (Credential) o;
-
- if (mCheckAAACert != that.mCheckAAACert) return false;
- if (mCtime != that.mCtime) return false;
- if (mExpTime != that.mExpTime) return false;
- if (mMachineManaged != that.mMachineManaged) return false;
- if (mShare != that.mShare) return false;
- if (mCertType != that.mCertType) return false;
- if (!mEAPMethod.equals(that.mEAPMethod)) return false;
- if (!Arrays.equals(mFingerPrint, that.mFingerPrint)) return false;
- if (!safeEquals(mImsi, that.mImsi)) {
- return false;
- }
-
- if (!mDisregardPassword && !safeEquals(mPassword, that.mPassword)) {
- return false;
- }
-
- if (!mRealm.equals(that.mRealm)) return false;
- if (!safeEquals(mSTokenApp, that.mSTokenApp)) {
- return false;
- }
- if (!safeEquals(mUserName, that.mUserName)) {
- return false;
- }
-
- return true;
- }
-
- private static boolean safeEquals(Object s1, Object s2) {
- if (s1 == null) {
- return s2 == null;
- }
- else {
- return s2 != null && s1.equals(s2);
- }
- }
-
- @Override
- public int hashCode() {
- int result = (int) (mCtime ^ (mCtime >>> 32));
- result = 31 * result + (int) (mExpTime ^ (mExpTime >>> 32));
- result = 31 * result + mRealm.hashCode();
- result = 31 * result + (mCheckAAACert ? 1 : 0);
- result = 31 * result + (mUserName != null ? mUserName.hashCode() : 0);
- result = 31 * result + (mPassword != null ? mPassword.hashCode() : 0);
- result = 31 * result + (mMachineManaged ? 1 : 0);
- result = 31 * result + (mSTokenApp != null ? mSTokenApp.hashCode() : 0);
- result = 31 * result + (mShare ? 1 : 0);
- result = 31 * result + mEAPMethod.hashCode();
- result = 31 * result + (mCertType != null ? mCertType.hashCode() : 0);
- result = 31 * result + (mFingerPrint != null ? Arrays.hashCode(mFingerPrint) : 0);
- result = 31 * result + (mImsi != null ? mImsi.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "Credential{" +
- "mCtime=" + Utils.toUTCString(mCtime) +
- ", mExpTime=" + Utils.toUTCString(mExpTime) +
- ", mRealm='" + mRealm + '\'' +
- ", mCheckAAACert=" + mCheckAAACert +
- ", mUserName='" + mUserName + '\'' +
- ", mPassword='" + mPassword + '\'' +
- ", mDisregardPassword=" + mDisregardPassword +
- ", mMachineManaged=" + mMachineManaged +
- ", mSTokenApp='" + mSTokenApp + '\'' +
- ", mShare=" + mShare +
- ", mEAPMethod=" + mEAPMethod +
- ", mCertType=" + mCertType +
- ", mFingerPrint=" + Utils.toHexString(mFingerPrint) +
- ", mImsi='" + mImsi + '\'' +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/pps/DomainMatcher.java b/service/java/com/android/server/wifi/hotspot2/pps/DomainMatcher.java
deleted file mode 100644
index 3f6e22a..0000000
--- a/service/java/com/android/server/wifi/hotspot2/pps/DomainMatcher.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package com.android.server.wifi.hotspot2.pps;
-
-import android.util.Log;
-
-import com.android.server.wifi.hotspot2.Utils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-
-public class DomainMatcher {
-
- public enum Match {None, Primary, Secondary}
-
- private final Label mRoot;
-
- private static class Label {
- private final Map<String, Label> mSubDomains;
- private final Match mMatch;
-
- private Label(Match match) {
- mMatch = match;
- mSubDomains = match == Match.None ? new HashMap<String, Label>() : null;
- }
-
- private void addDomain(Iterator<String> labels, Match match) {
- String labelName = labels.next();
- if (labels.hasNext()) {
- Label subLabel = new Label(Match.None);
- mSubDomains.put(labelName, subLabel);
- subLabel.addDomain(labels, match);
- } else {
- mSubDomains.put(labelName, new Label(match));
- }
- }
-
- private Label getSubLabel(String labelString) {
- return mSubDomains.get(labelString);
- }
-
- public Match getMatch() {
- return mMatch;
- }
-
- private void toString(StringBuilder sb) {
- if (mSubDomains != null) {
- sb.append(".{");
- for (Map.Entry<String, Label> entry : mSubDomains.entrySet()) {
- sb.append(entry.getKey());
- entry.getValue().toString(sb);
- }
- sb.append('}');
- } else {
- sb.append('=').append(mMatch);
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- toString(sb);
- return sb.toString();
- }
- }
-
- public DomainMatcher(List<String> primary, List<List<String>> secondary) {
- mRoot = new Label(Match.None);
- for (List<String> secondaryLabel : secondary) {
- mRoot.addDomain(secondaryLabel.iterator(), Match.Secondary);
- }
- // Primary overwrites secondary.
- mRoot.addDomain(primary.iterator(), Match.Primary);
- }
-
- /**
- * Check if domain is either a the same or a sub-domain of any of the domains in the domain tree
- * in this matcher, i.e. all or or a sub-set of the labels in domain matches a path in the tree.
- * @param domain Domain to be checked.
- * @return None if domain is not a sub-domain, Primary if it matched one of the primary domains
- * or Secondary if it matched on of the secondary domains.
- */
- public Match isSubDomain(List<String> domain) {
-
- Label label = mRoot;
- for (String labelString : domain) {
- label = label.getSubLabel(labelString);
- if (label == null) {
- return Match.None;
- } else if (label.getMatch() != Match.None) {
- return label.getMatch();
- }
- }
- return Match.None; // Domain is a super domain
- }
-
- public static boolean arg2SubdomainOfArg1(List<String> arg1, List<String> arg2) {
- if (arg2.size() < arg1.size()) {
- return false;
- }
-
- Iterator<String> l1 = arg1.iterator();
- Iterator<String> l2 = arg2.iterator();
-
- while(l1.hasNext()) {
- if (!l1.next().equals(l2.next())) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public String toString() {
- return "Domain matcher " + mRoot;
- }
-
- private static final String[] TestDomains = {
- "garbage.apple.com",
- "apple.com",
- "com",
- "jan.android.google.com.",
- "jan.android.google.com",
- "android.google.com",
- "google.com",
- "jan.android.google.net.",
- "jan.android.google.net",
- "android.google.net",
- "google.net",
- "net.",
- "."
- };
-
- public static void main(String[] args) {
- DomainMatcher dm1 = new DomainMatcher(Utils.splitDomain("android.google.com"),
- Collections.<List<String>>emptyList());
- for (String domain : TestDomains) {
- System.out.println(domain + ": " + dm1.isSubDomain(Utils.splitDomain(domain)));
- }
- List<List<String>> secondaries = new ArrayList<List<String>>();
- secondaries.add(Utils.splitDomain("apple.com"));
- secondaries.add(Utils.splitDomain("net"));
- DomainMatcher dm2 = new DomainMatcher(Utils.splitDomain("android.google.com"), secondaries);
- for (String domain : TestDomains) {
- System.out.println(domain + ": " + dm2.isSubDomain(Utils.splitDomain(domain)));
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/pps/HomeSP.java b/service/java/com/android/server/wifi/hotspot2/pps/HomeSP.java
deleted file mode 100644
index 160a056..0000000
--- a/service/java/com/android/server/wifi/hotspot2/pps/HomeSP.java
+++ /dev/null
@@ -1,374 +0,0 @@
-package com.android.server.wifi.hotspot2.pps;
-
-import android.util.Log;
-
-import com.android.server.wifi.SIMAccessor;
-import com.android.server.wifi.anqp.ANQPElement;
-import com.android.server.wifi.anqp.CellularNetwork;
-import com.android.server.wifi.anqp.Constants;
-import com.android.server.wifi.anqp.DomainNameElement;
-import com.android.server.wifi.anqp.NAIRealmElement;
-import com.android.server.wifi.anqp.RoamingConsortiumElement;
-import com.android.server.wifi.anqp.ThreeGPPNetworkElement;
-import com.android.server.wifi.hotspot2.AuthMatch;
-import com.android.server.wifi.hotspot2.NetworkDetail;
-import com.android.server.wifi.hotspot2.PasspointMatch;
-import com.android.server.wifi.hotspot2.Utils;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static com.android.server.wifi.anqp.Constants.ANQPElementType;
-
-/**
- * This object describes the Home SP sub tree in the "PerProviderSubscription MO" described in
- * the Hotspot 2.0 specification, section 9.1.
- * As a convenience, the object also refers to the other sub-parts of the full
- * PerProviderSubscription tree.
- */
-public class HomeSP {
- private final Map<String, Long> mSSIDs; // SSID, HESSID, [0,N]
- private final String mFQDN;
- private final DomainMatcher mDomainMatcher;
- private final Set<String> mOtherHomePartners;
- private final HashSet<Long> mRoamingConsortiums; // [0,N]
- private final Set<Long> mMatchAnyOIs; // [0,N]
- private final List<Long> mMatchAllOIs; // [0,N]
-
- private final Credential mCredential;
-
- // Informational:
- private final String mFriendlyName; // [1]
- private final String mIconURL; // [0,1]
-
- private final Policy mPolicy;
- private final int mCredentialPriority;
- private final Map<String, String> mAAATrustRoots;
- private final UpdateInfo mSubscriptionUpdate;
- private final SubscriptionParameters mSubscriptionParameters;
- private final int mUpdateIdentifier;
-
- @Deprecated
- public HomeSP(Map<String, Long> ssidMap,
- /*@NotNull*/ String fqdn,
- /*@NotNull*/ HashSet<Long> roamingConsortiums,
- /*@NotNull*/ Set<String> otherHomePartners,
- /*@NotNull*/ Set<Long> matchAnyOIs,
- /*@NotNull*/ List<Long> matchAllOIs,
- String friendlyName,
- String iconURL,
- Credential credential) {
-
- mSSIDs = ssidMap;
- List<List<String>> otherPartners = new ArrayList<>(otherHomePartners.size());
- for (String otherPartner : otherHomePartners) {
- otherPartners.add(Utils.splitDomain(otherPartner));
- }
- mOtherHomePartners = otherHomePartners;
- mFQDN = fqdn;
- mDomainMatcher = new DomainMatcher(Utils.splitDomain(fqdn), otherPartners);
- mRoamingConsortiums = roamingConsortiums;
- mMatchAnyOIs = matchAnyOIs;
- mMatchAllOIs = matchAllOIs;
- mFriendlyName = friendlyName;
- mIconURL = iconURL;
- mCredential = credential;
-
- mPolicy = null;
- mCredentialPriority = -1;
- mAAATrustRoots = null;
- mSubscriptionUpdate = null;
- mSubscriptionParameters = null;
- mUpdateIdentifier = -1;
- }
-
- public HomeSP(Map<String, Long> ssidMap,
- /*@NotNull*/ String fqdn,
- /*@NotNull*/ HashSet<Long> roamingConsortiums,
- /*@NotNull*/ Set<String> otherHomePartners,
- /*@NotNull*/ Set<Long> matchAnyOIs,
- /*@NotNull*/ List<Long> matchAllOIs,
- String friendlyName,
- String iconURL,
- Credential credential,
-
- Policy policy,
- int credentialPriority,
- Map<String, String> AAATrustRoots,
- UpdateInfo subscriptionUpdate,
- SubscriptionParameters subscriptionParameters,
- int updateIdentifier) {
-
- mSSIDs = ssidMap;
- List<List<String>> otherPartners = new ArrayList<>(otherHomePartners.size());
- for (String otherPartner : otherHomePartners) {
- otherPartners.add(Utils.splitDomain(otherPartner));
- }
- mOtherHomePartners = otherHomePartners;
- mFQDN = fqdn;
- mDomainMatcher = new DomainMatcher(Utils.splitDomain(fqdn), otherPartners);
- mRoamingConsortiums = roamingConsortiums;
- mMatchAnyOIs = matchAnyOIs;
- mMatchAllOIs = matchAllOIs;
- mFriendlyName = friendlyName;
- mIconURL = iconURL;
- mCredential = credential;
-
- mPolicy = policy;
- mCredentialPriority = credentialPriority;
- mAAATrustRoots = AAATrustRoots;
- mSubscriptionUpdate = subscriptionUpdate;
- mSubscriptionParameters = subscriptionParameters;
- mUpdateIdentifier = updateIdentifier;
- }
-
- public int getUpdateIdentifier() {
- return mUpdateIdentifier;
- }
-
- public UpdateInfo getSubscriptionUpdate() {
- return mSubscriptionUpdate;
- }
-
- public Policy getPolicy() {
- return mPolicy;
- }
-
- public PasspointMatch match(NetworkDetail networkDetail,
- Map<ANQPElementType, ANQPElement> anqpElementMap,
- SIMAccessor simAccessor) {
-
- List<String> imsis = simAccessor.getMatchingImsis(mCredential.getImsi());
-
- PasspointMatch spMatch = matchSP(networkDetail, anqpElementMap, imsis);
-
- if (spMatch == PasspointMatch.Incomplete || spMatch == PasspointMatch.Declined) {
- return spMatch;
- }
-
- if (imsiMatch(imsis, (ThreeGPPNetworkElement)
- anqpElementMap.get(ANQPElementType.ANQP3GPPNetwork)) != null) {
- // PLMN match, promote sp match to roaming if necessary.
- return spMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider : spMatch;
- }
-
- NAIRealmElement naiRealmElement =
- (NAIRealmElement) anqpElementMap.get(ANQPElementType.ANQPNAIRealm);
-
- int authMatch = naiRealmElement != null ?
- naiRealmElement.match(mCredential) :
- AuthMatch.Indeterminate;
-
- Log.d(Utils.hs2LogTag(getClass()), networkDetail.toKeyString() + " match on " + mFQDN +
- ": " + spMatch + ", auth " + AuthMatch.toString(authMatch));
-
- if (authMatch == AuthMatch.None) {
- // Distinct auth mismatch, demote authentication.
- return PasspointMatch.None;
- }
- else if ((authMatch & AuthMatch.Realm) == 0) {
- // No realm match, return sp match as is.
- return spMatch;
- }
- else {
- // Realm match, promote sp match to roaming if necessary.
- return spMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider : spMatch;
- }
- }
-
- public PasspointMatch matchSP(NetworkDetail networkDetail,
- Map<ANQPElementType, ANQPElement> anqpElementMap,
- List<String> imsis) {
-
- if (mSSIDs.containsKey(networkDetail.getSSID())) {
- Long hessid = mSSIDs.get(networkDetail.getSSID());
- if (hessid == null || networkDetail.getHESSID() == hessid) {
- Log.d(Utils.hs2LogTag(getClass()), "match SSID");
- return PasspointMatch.HomeProvider;
- }
- }
-
- Set<Long> anOIs = new HashSet<>();
-
- if (networkDetail.getRoamingConsortiums() != null) {
- for (long oi : networkDetail.getRoamingConsortiums()) {
- anOIs.add(oi);
- }
- }
-
- boolean validANQP = anqpElementMap != null &&
- Constants.hasBaseANQPElements(anqpElementMap.keySet());
-
- RoamingConsortiumElement rcElement = validANQP ?
- (RoamingConsortiumElement) anqpElementMap.get(ANQPElementType.ANQPRoamingConsortium)
- : null;
- if (rcElement != null) {
- anOIs.addAll(rcElement.getOIs());
- }
-
- // It may seem reasonable to check for home provider match prior to checking for roaming
- // relationship, but it is possible to avoid an ANQP query if it turns out that the
- // "match all" rule fails based only on beacon info only.
- boolean roamingMatch = false;
-
- if (!mMatchAllOIs.isEmpty()) {
- boolean matchesAll = true;
-
- for (long spOI : mMatchAllOIs) {
- if (!anOIs.contains(spOI)) {
- matchesAll = false;
- break;
- }
- }
- if (matchesAll) {
- roamingMatch = true;
- }
- else {
- if (validANQP || networkDetail.getAnqpOICount() == 0) {
- return PasspointMatch.Declined;
- }
- else {
- return PasspointMatch.Incomplete;
- }
- }
- }
-
- if (!roamingMatch &&
- (!Collections.disjoint(mMatchAnyOIs, anOIs) ||
- !Collections.disjoint(mRoamingConsortiums, anOIs))) {
- roamingMatch = true;
- }
-
- if (!validANQP) {
- return PasspointMatch.Incomplete;
- }
-
- DomainNameElement domainNameElement =
- (DomainNameElement) anqpElementMap.get(ANQPElementType.ANQPDomName);
-
- if (domainNameElement != null) {
- for (String domain : domainNameElement.getDomains()) {
- List<String> anLabels = Utils.splitDomain(domain);
- DomainMatcher.Match match = mDomainMatcher.isSubDomain(anLabels);
- if (match != DomainMatcher.Match.None) {
- return PasspointMatch.HomeProvider;
- }
-
- if (imsiMatch(imsis, anLabels) != null) {
- return PasspointMatch.HomeProvider;
- }
- }
- }
-
- return roamingMatch ? PasspointMatch.RoamingProvider : PasspointMatch.None;
- }
-
- private String imsiMatch(List<String> imsis, ThreeGPPNetworkElement plmnElement) {
- if (imsis == null || plmnElement == null || plmnElement.getPlmns().isEmpty()) {
- return null;
- }
- for (CellularNetwork network : plmnElement.getPlmns()) {
- for (String mccMnc : network) {
- String imsi = imsiMatch(imsis, mccMnc);
- if (imsi != null) {
- return imsi;
- }
- }
- }
- return null;
- }
-
- private String imsiMatch(List<String> imsis, List<String> fqdn) {
- if (imsis == null) {
- return null;
- }
- String mccMnc = Utils.getMccMnc(fqdn);
- return mccMnc != null ? imsiMatch(imsis, mccMnc) : null;
- }
-
- private String imsiMatch(List<String> imsis, String mccMnc) {
- if (mCredential.getImsi().matchesMccMnc(mccMnc)) {
- for (String imsi : imsis) {
- if (imsi.startsWith(mccMnc)) {
- return imsi;
- }
- }
- }
- return null;
- }
-
- public String getFQDN() { return mFQDN; }
- public String getFriendlyName() { return mFriendlyName; }
- public HashSet<Long> getRoamingConsortiums() { return mRoamingConsortiums; }
- public Credential getCredential() { return mCredential; }
-
- public Map<String, Long> getSSIDs() {
- return mSSIDs;
- }
-
- public Collection<String> getOtherHomePartners() {
- return mOtherHomePartners;
- }
-
- public Set<Long> getMatchAnyOIs() {
- return mMatchAnyOIs;
- }
-
- public List<Long> getMatchAllOIs() {
- return mMatchAllOIs;
- }
-
- public String getIconURL() {
- return mIconURL;
- }
-
- public boolean deepEquals(HomeSP other) {
- return mFQDN.equals(other.mFQDN) &&
- mSSIDs.equals(other.mSSIDs) &&
- mOtherHomePartners.equals(other.mOtherHomePartners) &&
- mRoamingConsortiums.equals(other.mRoamingConsortiums) &&
- mMatchAnyOIs.equals(other.mMatchAnyOIs) &&
- mMatchAllOIs.equals(other.mMatchAllOIs) &&
- mFriendlyName.equals(other.mFriendlyName) &&
- Utils.compare(mIconURL, other.mIconURL) == 0 &&
- mCredential.equals(other.mCredential);
- }
-
- @Override
- public boolean equals(Object thatObject) {
- if (this == thatObject) {
- return true;
- } else if (thatObject == null || getClass() != thatObject.getClass()) {
- return false;
- }
-
- HomeSP that = (HomeSP) thatObject;
- return mFQDN.equals(that.mFQDN);
- }
-
- @Override
- public int hashCode() {
- return mFQDN.hashCode();
- }
-
- @Override
- public String toString() {
- return "HomeSP{" +
- "SSIDs=" + mSSIDs +
- ", FQDN='" + mFQDN + '\'' +
- ", DomainMatcher=" + mDomainMatcher +
- ", RoamingConsortiums={" + Utils.roamingConsortiumsToString(mRoamingConsortiums) +
- '}' +
- ", MatchAnyOIs={" + Utils.roamingConsortiumsToString(mMatchAnyOIs) + '}' +
- ", MatchAllOIs={" + Utils.roamingConsortiumsToString(mMatchAllOIs) + '}' +
- ", Credential=" + mCredential +
- ", FriendlyName='" + mFriendlyName + '\'' +
- ", IconURL='" + mIconURL + '\'' +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/pps/Policy.java b/service/java/com/android/server/wifi/hotspot2/pps/Policy.java
deleted file mode 100644
index 186afd1..0000000
--- a/service/java/com/android/server/wifi/hotspot2/pps/Policy.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package com.android.server.wifi.hotspot2.pps;
-
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.hotspot2.omadm.OMAException;
-import com.android.server.wifi.hotspot2.omadm.OMANode;
-import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_Country;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_DLBandwidth;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_FQDN_Match;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_IPProtocol;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_MaximumBSSLoadValue;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_MinBackhaulThreshold;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_NetworkType;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_PolicyUpdate;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_PortNumber;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_PreferredRoamingPartnerList;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_Priority;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_RequiredProtoPortTuple;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_SPExclusionList;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_SSID;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_ULBandwidth;
-
-public class Policy {
- private final List<PreferredRoamingPartner> mPreferredRoamingPartners;
- private final List<MinBackhaul> mMinBackhaulThresholds;
- private final UpdateInfo mPolicyUpdate;
- private final List<String> mSPExclusionList;
- private final Map<Integer, List<Integer>> mRequiredProtos;
- private final int mMaxBSSLoad;
-
- public Policy(OMANode node) throws OMAException {
-
- OMANode rpNode = node.getChild(TAG_PreferredRoamingPartnerList);
- if (rpNode == null) {
- mPreferredRoamingPartners = null;
- }
- else {
- mPreferredRoamingPartners = new ArrayList<>(rpNode.getChildren().size());
- for (OMANode instance : rpNode.getChildren()) {
- if (instance.isLeaf()) {
- throw new OMAException("Not expecting leaf node in " +
- TAG_PreferredRoamingPartnerList);
- }
- mPreferredRoamingPartners.add(new PreferredRoamingPartner(instance));
- }
- }
-
- OMANode bhtNode = node.getChild(TAG_MinBackhaulThreshold);
- if (bhtNode == null) {
- mMinBackhaulThresholds = null;
- }
- else {
- mMinBackhaulThresholds = new ArrayList<>(bhtNode.getChildren().size());
- for (OMANode instance : bhtNode.getChildren()) {
- if (instance.isLeaf()) {
- throw new OMAException("Not expecting leaf node in " +
- TAG_MinBackhaulThreshold);
- }
- mMinBackhaulThresholds.add(new MinBackhaul(instance));
- }
- }
-
- mPolicyUpdate = new UpdateInfo(node.getChild(TAG_PolicyUpdate));
-
- OMANode sxNode = node.getChild(TAG_SPExclusionList);
- if (sxNode == null) {
- mSPExclusionList = null;
- }
- else {
- mSPExclusionList = new ArrayList<>(sxNode.getChildren().size());
- for (OMANode instance : sxNode.getChildren()) {
- if (instance.isLeaf()) {
- throw new OMAException("Not expecting leaf node in " + TAG_SPExclusionList);
- }
- mSPExclusionList
- .add(PasspointManagementObjectManager.getString(instance, TAG_SSID));
- }
- }
-
- OMANode rptNode = node.getChild(TAG_RequiredProtoPortTuple);
- if (rptNode == null) {
- mRequiredProtos = null;
- }
- else {
- mRequiredProtos = new HashMap<>(rptNode.getChildren().size());
- for (OMANode instance : rptNode.getChildren()) {
- if (instance.isLeaf()) {
- throw new OMAException("Not expecting leaf node in " +
- TAG_RequiredProtoPortTuple);
- }
- int protocol =
- (int) PasspointManagementObjectManager
- .getLong(instance, TAG_IPProtocol, null);
- String[] portSegments =
- PasspointManagementObjectManager
- .getString(instance, TAG_PortNumber).split(",");
- List<Integer> ports = new ArrayList<>(portSegments.length);
- for (String portSegment : portSegments) {
- try {
- ports.add(Integer.parseInt(portSegment));
- }
- catch (NumberFormatException nfe) {
- throw new OMAException("Port is not a number: " + portSegment);
- }
- }
- mRequiredProtos.put(protocol, ports);
- }
- }
-
- mMaxBSSLoad = (int) PasspointManagementObjectManager.getLong(node,
- TAG_MaximumBSSLoadValue, Long.MAX_VALUE);
- }
-
- public List<PreferredRoamingPartner> getPreferredRoamingPartners() {
- return mPreferredRoamingPartners;
- }
-
- public List<MinBackhaul> getMinBackhaulThresholds() {
- return mMinBackhaulThresholds;
- }
-
- public UpdateInfo getPolicyUpdate() {
- return mPolicyUpdate;
- }
-
- public List<String> getSPExclusionList() {
- return mSPExclusionList;
- }
-
- public Map<Integer, List<Integer>> getRequiredProtos() {
- return mRequiredProtos;
- }
-
- public int getMaxBSSLoad() {
- return mMaxBSSLoad;
- }
-
- private static class PreferredRoamingPartner {
- private final List<String> mDomain;
- private final Boolean mIncludeSubDomains;
- private final int mPriority;
- private final String mCountry;
-
- private PreferredRoamingPartner(OMANode node)
- throws OMAException {
-
- String[] segments =
- PasspointManagementObjectManager.getString(node, TAG_FQDN_Match).split(",");
- if (segments.length != 2) {
- throw new OMAException("Bad FQDN match string: " + TAG_FQDN_Match);
- }
- mDomain = Utils.splitDomain(segments[0]);
- mIncludeSubDomains =
- PasspointManagementObjectManager.getSelection(TAG_FQDN_Match, segments[1]);
- mPriority = (int) PasspointManagementObjectManager.getLong(node, TAG_Priority, null);
- mCountry = PasspointManagementObjectManager.getString(node, TAG_Country);
- }
-
- @Override
- public String toString() {
- return "PreferredRoamingPartner{" +
- "domain=" + mDomain +
- ", includeSubDomains=" + mIncludeSubDomains +
- ", priority=" + mPriority +
- ", country='" + mCountry + '\'' +
- '}';
- }
- }
-
- private static class MinBackhaul {
- private final Boolean mHome;
- private final long mDL;
- private final long mUL;
-
- private MinBackhaul(OMANode node) throws OMAException {
- mHome = PasspointManagementObjectManager.getSelection(node, TAG_NetworkType);
- mDL = PasspointManagementObjectManager.getLong(node, TAG_DLBandwidth, Long.MAX_VALUE);
- mUL = PasspointManagementObjectManager.getLong(node, TAG_ULBandwidth, Long.MAX_VALUE);
- }
-
- @Override
- public String toString() {
- return "MinBackhaul{" +
- "home=" + mHome +
- ", DL=" + mDL +
- ", UL=" + mUL +
- '}';
- }
- }
-
- @Override
- public String toString() {
- return "Policy{" +
- "preferredRoamingPartners=" + mPreferredRoamingPartners +
- ", minBackhaulThresholds=" + mMinBackhaulThresholds +
- ", policyUpdate=" + mPolicyUpdate +
- ", SPExclusionList=" + mSPExclusionList +
- ", requiredProtos=" + mRequiredProtos +
- ", maxBSSLoad=" + mMaxBSSLoad +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/pps/SubscriptionParameters.java b/service/java/com/android/server/wifi/hotspot2/pps/SubscriptionParameters.java
deleted file mode 100644
index 2e44d5d..0000000
--- a/service/java/com/android/server/wifi/hotspot2/pps/SubscriptionParameters.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.android.server.wifi.hotspot2.pps;
-
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.hotspot2.omadm.OMAException;
-import com.android.server.wifi.hotspot2.omadm.OMANode;
-import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_CreationDate;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_DataLimit;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_ExpirationDate;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_StartDate;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_TimeLimit;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_TypeOfSubscription;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_UsageLimits;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_UsageTimePeriod;
-
-public class SubscriptionParameters {
- private final long mCDate;
- private final long mXDate;
- private final String mType;
- private final List<Limit> mLimits;
-
- public SubscriptionParameters(OMANode node) throws OMAException {
- mCDate = PasspointManagementObjectManager.getTime(node.getChild(TAG_CreationDate));
- mXDate = PasspointManagementObjectManager.getTime(node.getChild(TAG_ExpirationDate));
- mType = PasspointManagementObjectManager.getString(node.getChild(TAG_TypeOfSubscription));
-
- OMANode ulNode = node.getChild(TAG_UsageLimits);
- if (ulNode == null) {
- mLimits = null;
- }
- else {
- mLimits = new ArrayList<>(ulNode.getChildren().size());
- for (OMANode instance : ulNode.getChildren()) {
- if (instance.isLeaf()) {
- throw new OMAException("Not expecting leaf node in " +
- TAG_UsageLimits);
- }
- mLimits.add(new Limit(instance));
- }
- }
-
- }
-
- private static class Limit {
- private final long mDataLimit;
- private final long mStartDate;
- private final long mTimeLimit;
- private final long mUsageTimePeriod;
-
- private Limit(OMANode node) throws OMAException {
- mDataLimit = PasspointManagementObjectManager
- .getLong(node, TAG_DataLimit, Long.MAX_VALUE);
- mStartDate = PasspointManagementObjectManager
- .getTime(node.getChild(TAG_StartDate));
- mTimeLimit = PasspointManagementObjectManager
- .getLong(node, TAG_TimeLimit, Long.MAX_VALUE)
- * PasspointManagementObjectManager.IntervalFactor;
- mUsageTimePeriod = PasspointManagementObjectManager
- .getLong(node, TAG_UsageTimePeriod, null);
- }
-
- @Override
- public String toString() {
- return "Limit{" +
- "dataLimit=" + mDataLimit +
- ", startDate=" + Utils.toUTCString(mStartDate) +
- ", timeLimit=" + mTimeLimit +
- ", usageTimePeriod=" + mUsageTimePeriod +
- '}';
- }
- }
-
- @Override
- public String toString() {
- return "SubscriptionParameters{" +
- "cDate=" + Utils.toUTCString(mCDate) +
- ", xDate=" + Utils.toUTCString(mXDate) +
- ", type='" + mType + '\'' +
- ", limits=" + mLimits +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/hotspot2/pps/UpdateInfo.java b/service/java/com/android/server/wifi/hotspot2/pps/UpdateInfo.java
deleted file mode 100644
index 99861dd..0000000
--- a/service/java/com/android/server/wifi/hotspot2/pps/UpdateInfo.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.android.server.wifi.hotspot2.pps;
-
-import android.util.Base64;
-
-import com.android.server.wifi.hotspot2.Utils;
-import com.android.server.wifi.hotspot2.omadm.OMAException;
-import com.android.server.wifi.hotspot2.omadm.OMANode;
-import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager;
-
-import java.nio.charset.StandardCharsets;
-
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_CertSHA256Fingerprint;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_CertURL;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_Password;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_Restriction;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_TrustRoot;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_URI;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_UpdateInterval;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_UpdateMethod;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_Username;
-import static com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager.TAG_UsernamePassword;
-
-public class UpdateInfo {
- public enum UpdateRestriction {HomeSP, RoamingPartner, Unrestricted}
-
- private final long mInterval;
- private final boolean mSPPClientInitiated;
- private final UpdateRestriction mUpdateRestriction;
- private final String mURI;
- private final String mUsername;
- private final String mPassword;
- private final String mCertURL;
- private final String mCertFP;
-
- public UpdateInfo(OMANode policyUpdate) throws OMAException {
- mInterval = PasspointManagementObjectManager.getLong(policyUpdate, TAG_UpdateInterval, null)
- * PasspointManagementObjectManager.IntervalFactor;
- mSPPClientInitiated = PasspointManagementObjectManager.getSelection(policyUpdate,
- TAG_UpdateMethod);
- mUpdateRestriction =
- PasspointManagementObjectManager.getSelection(policyUpdate, TAG_Restriction);
- mURI = PasspointManagementObjectManager.getString(policyUpdate, TAG_URI);
-
- OMANode unp = policyUpdate.getChild(TAG_UsernamePassword);
- if (unp != null) {
- mUsername = PasspointManagementObjectManager.getString(unp.getChild(TAG_Username));
- String pw = PasspointManagementObjectManager.getString(unp.getChild(TAG_Password));
- mPassword = new String(Base64.decode(pw.getBytes(StandardCharsets.US_ASCII),
- Base64.DEFAULT), StandardCharsets.UTF_8);
- }
- else {
- mUsername = null;
- mPassword = null;
- }
-
- OMANode trustRoot = PasspointManagementObjectManager.getChild(policyUpdate, TAG_TrustRoot);
- mCertURL = PasspointManagementObjectManager.getString(trustRoot, TAG_CertURL);
- mCertFP = PasspointManagementObjectManager.getString(trustRoot, TAG_CertSHA256Fingerprint);
- }
-
- public long getInterval() {
- return mInterval;
- }
-
- public boolean isSPPClientInitiated() {
- return mSPPClientInitiated;
- }
-
- public UpdateRestriction getUpdateRestriction() {
- return mUpdateRestriction;
- }
-
- public String getURI() {
- return mURI;
- }
-
- public String getUsername() {
- return mUsername;
- }
-
- public String getPassword() {
- return mPassword;
- }
-
- public String getCertURL() {
- return mCertURL;
- }
-
- public String getCertFP() {
- return mCertFP;
- }
-
- @Override
- public String toString() {
- return "UpdateInfo{" +
- "interval=" + Utils.toHMS(mInterval) +
- ", SPPClientInitiated=" + mSPPClientInitiated +
- ", updateRestriction=" + mUpdateRestriction +
- ", URI='" + mURI + '\'' +
- ", username='" + mUsername + '\'' +
- ", password=" + mPassword +
- ", certURL='" + mCertURL + '\'' +
- ", certFP='" + mCertFP + '\'' +
- '}';
- }
-}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanClientState.java b/service/java/com/android/server/wifi/nan/WifiNanClientState.java
deleted file mode 100644
index 0707270..0000000
--- a/service/java/com/android/server/wifi/nan/WifiNanClientState.java
+++ /dev/null
@@ -1,180 +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.nan;
-
-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.os.RemoteException;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-public class WifiNanClientState {
- private static final String TAG = "WifiNanClientState";
- private static final boolean DBG = false;
- private static final boolean VDBG = false; // STOPSHIP if true
-
- /* 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 final SparseArray<WifiNanSessionState> mSessions = new SparseArray<>();
-
- private int mUid;
- private ConfigRequest mConfigRequest;
-
- public WifiNanClientState(int uid, IWifiNanEventListener listener, int events) {
- mUid = uid;
- mListener = listener;
- mEvents = events;
- }
-
- public void destroy() {
- mListener = null;
- for (int i = 0; i < mSessions.size(); ++i) {
- mSessions.valueAt(i).destroy();
- }
- mSessions.clear();
- mConfigRequest = null;
- }
-
- public void setConfigRequest(ConfigRequest configRequest) {
- mConfigRequest = configRequest;
- }
-
- public ConfigRequest getConfigRequest() {
- return mConfigRequest;
- }
-
- public int getUid() {
- return mUid;
- }
-
- public WifiNanSessionState getNanSessionStateForPubSubId(int pubSubId) {
- for (int i = 0; i < mSessions.size(); ++i) {
- WifiNanSessionState session = mSessions.valueAt(i);
- if (session.isPubSubIdSession(pubSubId)) {
- return session;
- }
- }
-
- 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);
- }
-
- mSessions.put(sessionId, new WifiNanSessionState(sessionId, listener, events));
- }
-
- public void destroySession(int sessionId) {
- WifiNanSessionState session = mSessions.get(sessionId);
- if (session == null) {
- Log.e(TAG, "destroySession: sessionId doesn't exist - " + sessionId);
- return;
- }
-
- mSessions.delete(sessionId);
- session.destroy();
- }
-
- public WifiNanSessionState getSession(int sessionId) {
- return mSessions.get(sessionId);
- }
-
- public void onConfigCompleted(ConfigRequest completedConfig) {
- if (mListener != null && (mEvents & WifiNanEventListener.LISTEN_CONFIG_COMPLETED) != 0) {
- 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();
- } catch (RemoteException e) {
- Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
- }
-
- return 1;
- }
-
- return 0;
- }
-
- public int onClusterChange(int flag, byte[] mac) {
- if (mListener != null && (mEvents & WifiNanEventListener.LISTEN_IDENTITY_CHANGED) != 0) {
- try {
- mListener.onIdentityChanged();
- } catch (RemoteException e) {
- Log.w(TAG, "onIdentityChanged: RemoteException - ignored: " + e);
- }
-
- return 1;
- }
-
- return 0;
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("NanClientState:");
- pw.println(" mUid: " + mUid);
- pw.println(" mConfigRequest: " + mConfigRequest);
- pw.println(" mListener: " + mListener);
- pw.println(" mEvents: " + mEvents);
- 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/WifiNanNative.java b/service/java/com/android/server/wifi/nan/WifiNanNative.java
deleted file mode 100644
index 8715719..0000000
--- a/service/java/com/android/server/wifi/nan/WifiNanNative.java
+++ /dev/null
@@ -1,627 +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.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.util.Log;
-
-import com.android.server.wifi.WifiNative;
-
-import libcore.util.HexEncoding;
-
-/**
- * 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";
- private static final boolean DBG = false;
- private static final boolean VDBG = false; // STOPSHIP if true
-
- private static final int WIFI_SUCCESS = 0;
-
- private static boolean sNanNativeInit = false;
-
- private static WifiNanNative sWifiNanNativeSingleton;
-
- private static native int registerNanNatives();
-
- 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
- // initialization.
- WifiNative dummy = WifiNative.getWlanNativeInterface();
- if (dummy == null) {
- Log.w(TAG, "can't get access to WifiNative");
- return null;
- }
-
- if (sWifiNanNativeSingleton == null) {
- sWifiNanNativeSingleton = new WifiNanNative();
- registerNanNatives();
- }
-
- return sWifiNanNativeSingleton;
- }
-
- public static class Capabilities {
- public int maxConcurrentNanClusters;
- public int maxPublishes;
- public int maxSubscribes;
- public int maxServiceNameLen;
- public int maxMatchFilterLen;
- public int maxTotalMatchFilterLen;
- public int maxServiceSpecificInfoLen;
- public int maxVsaDataLen;
- public int maxMeshDataLen;
- public int maxNdiInterfaces;
- public int maxNdpSessions;
- public int maxAppInfoLen;
-
- @Override
- public String toString() {
- return "Capabilities [maxConcurrentNanClusters=" + maxConcurrentNanClusters
- + ", maxPublishes=" + maxPublishes + ", maxSubscribes=" + maxSubscribes
- + ", maxServiceNameLen=" + maxServiceNameLen + ", maxMatchFilterLen="
- + maxMatchFilterLen + ", maxTotalMatchFilterLen=" + maxTotalMatchFilterLen
- + ", maxServiceSpecificInfoLen=" + maxServiceSpecificInfoLen
- + ", maxVsaDataLen=" + maxVsaDataLen + ", maxMeshDataLen=" + maxMeshDataLen
- + ", maxNdiInterfaces=" + maxNdiInterfaces + ", maxNdpSessions="
- + maxNdpSessions + ", maxAppInfoLen=" + maxAppInfoLen + "]";
- }
- }
-
- /* package */ static native int initNanHandlersNative(Object 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");
- synchronized (WifiNative.sLock) {
- boolean halStarted = WifiNative.getWlanNativeInterface().isHalStarted();
- if (!halStarted) {
- halStarted = WifiNative.getWlanNativeInterface().startHal();
- }
- if (halStarted) {
- int ret = initNanHandlersNative(WifiNative.class, WifiNative.sWlan0Index);
- if (DBG) Log.d(TAG, "initNanHandlersNative: res=" + ret);
- sNanNativeInit = ret == WIFI_SUCCESS;
-
- if (sNanNativeInit) {
- ret = getCapabilitiesNative(
- WifiNanStateManager.getInstance().createNextTransactionId(),
- WifiNative.class,
- WifiNative.sWlan0Index);
- if (DBG) Log.d(TAG, "getCapabilitiesNative: res=" + ret);
- }
-
- return sNanNativeInit;
- } else {
- Log.w(TAG, "isNanInit: HAL not initialized");
- return false;
- }
- }
- }
-
- private WifiNanNative() {
- // do nothing
- }
-
- private static native int enableAndConfigureNative(short transactionId, Object cls, int iface,
- ConfigRequest configRequest);
-
- public void enableAndConfigure(short transactionId, ConfigRequest configRequest) {
- boolean success;
-
- if (VDBG) Log.d(TAG, "enableAndConfigure: configRequest=" + configRequest);
- if (isNanInit(true)) {
- int ret;
- synchronized (WifiNative.sLock) {
- ret = enableAndConfigureNative(transactionId, WifiNative.class,
- WifiNative.sWlan0Index, configRequest);
- }
- if (DBG) Log.d(TAG, "enableAndConfigureNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
- } else {
- Log.w(TAG, "enableAndConfigure: NanInit fails");
- success = false;
- }
-
-
- // TODO: do something on !success - send failure message back
- }
-
- private static native int disableNative(short transactionId, Object cls, int iface);
-
- public void disable(short transactionId) {
- boolean success;
-
- if (VDBG) Log.d(TAG, "disableNan");
- if (isNanInit(true)) {
- int ret;
- synchronized (WifiNative.sLock) {
- ret = disableNative(transactionId, WifiNative.class, WifiNative.sWlan0Index);
- }
- if (DBG) Log.d(TAG, "disableNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
- } else {
- Log.w(TAG, "disable: cannot initialize NAN");
- success = 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);
-
- public void publish(short transactionId, int publishId, PublishData publishData,
- PublishSettings publishSettings) {
- boolean success;
-
- if (VDBG) {
- Log.d(TAG, "publish: transactionId=" + transactionId + ",data='" + publishData
- + "', settings=" + publishSettings);
- }
-
- if (isNanInit(true)) {
- int ret;
- synchronized (WifiNative.sLock) {
- ret = publishNative(transactionId, publishId, WifiNative.class,
- WifiNative.sWlan0Index, publishData, publishSettings);
- }
- if (DBG) Log.d(TAG, "publishNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
- } else {
- Log.w(TAG, "publish: cannot initialize NAN");
- success = 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);
-
- public void subscribe(short transactionId, int subscribeId, SubscribeData subscribeData,
- SubscribeSettings subscribeSettings) {
- boolean success;
-
- if (VDBG) {
- Log.d(TAG, "subscribe: transactionId=" + transactionId + ", data='" + subscribeData
- + "', settings=" + subscribeSettings);
- }
-
- if (isNanInit(true)) {
- int ret;
- synchronized (WifiNative.sLock) {
- ret = subscribeNative(transactionId, subscribeId, WifiNative.class,
- WifiNative.sWlan0Index, subscribeData, subscribeSettings);
- }
- if (DBG) Log.d(TAG, "subscribeNative: ret=" + ret);
- success = ret == WIFI_SUCCESS;
- } else {
- Log.w(TAG, "subscribe: cannot initialize NAN");
- success = 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);
-
- public void sendMessage(short transactionId, int pubSubId, int requestorInstanceId, byte[] dest,
- byte[] message, int messageLength) {
- boolean success;
-
- if (VDBG) {
- Log.d(TAG,
- "sendMessage: transactionId=" + transactionId + ", pubSubId=" + pubSubId
- + ", requestorInstanceId=" + requestorInstanceId + ", dest="
- + String.valueOf(HexEncoding.encode(dest)) + ", messageLength="
- + messageLength);
- }
-
- if (isNanInit(true)) {
- 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;
- } else {
- Log.w(TAG, "sendMessage: cannot initialize NAN");
- success = false;
- }
-
- // TODO: do something on !success - send failure message back
- }
-
- private static native int stopPublishNative(short transactionId, Object cls, int iface,
- int pubSubId);
-
- public void stopPublish(short transactionId, int pubSubId) {
- boolean success;
-
- if (VDBG) {
- Log.d(TAG, "stopPublish: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
- }
-
- if (isNanInit(true)) {
- 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;
- } else {
- Log.w(TAG, "stopPublish: cannot initialize NAN");
- success = false;
- }
-
- // TODO: do something on !success - send failure message back
- }
-
- private static native int stopSubscribeNative(short transactionId, Object cls, int iface,
- int pubSubId);
-
- public void stopSubscribe(short transactionId, int pubSubId) {
- boolean success;
-
- if (VDBG) {
- Log.d(TAG, "stopSubscribe: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
- }
-
- if (isNanInit(true)) {
- 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;
- } else {
- Log.w(TAG, "stopSubscribe: cannot initialize NAN");
- success = false;
- }
-
- // TODO: do something on !success - send failure message back
- }
-
- // EVENTS
-
- // NanResponseType for API responses: will add values as needed
- public static final int NAN_RESPONSE_ENABLED = 0;
- public static final int NAN_RESPONSE_PUBLISH = 2;
- public static final int NAN_RESPONSE_PUBLISH_CANCEL = 3;
- 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_GET_CAPABILITIES = 12;
-
- // direct copy from wifi_nan.h: need to keep in sync
- 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;
- public static final int NAN_STATUS_INVALID_MSG_VERSION = 3;
- public static final int NAN_STATUS_INVALID_MSG_LEN = 4;
- public static final int NAN_STATUS_INVALID_MSG_ID = 5;
- public static final int NAN_STATUS_INVALID_HANDLE = 6;
- public static final int NAN_STATUS_NO_SPACE_AVAILABLE = 7;
- public static final int NAN_STATUS_INVALID_PUBLISH_TYPE = 8;
- public static final int NAN_STATUS_INVALID_TX_TYPE = 9;
- public static final int NAN_STATUS_INVALID_MATCH_ALGORITHM = 10;
- public static final int NAN_STATUS_DISABLE_IN_PROGRESS = 11;
- public static final int NAN_STATUS_INVALID_TLV_LEN = 12;
- public static final int NAN_STATUS_INVALID_TLV_TYPE = 13;
- public static final int NAN_STATUS_MISSING_TLV_TYPE = 14;
- public static final int NAN_STATUS_INVALID_TOTAL_TLVS_LEN = 15;
- public static final int NAN_STATUS_INVALID_MATCH_HANDLE = 16;
- 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;
-
- // 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;
- public static final int NAN_STATUS_INVALID_MASTER_PREFERENCE_VALUE = 4099;
- public static final int NAN_STATUS_INVALID_LOW_CLUSTER_ID_VALUE = 4100;
- public static final int NAN_STATUS_INVALID_HIGH_CLUSTER_ID_VALUE = 4101;
- public static final int NAN_STATUS_INVALID_BACKGROUND_SCAN_PERIOD = 4102;
- public static final int NAN_STATUS_INVALID_RSSI_PROXIMITY_VALUE = 4103;
- public static final int NAN_STATUS_INVALID_SCAN_CHANNEL = 4104;
- public static final int NAN_STATUS_INVALID_POST_NAN_CONNECTIVITY_CAPABILITIES_BITMAP = 4105;
- public static final int NAN_STATUS_INVALID_FA_MAP_NUMCHAN_VALUE = 4106;
- public static final int NAN_STATUS_INVALID_FA_MAP_DURATION_VALUE = 4107;
- public static final int NAN_STATUS_INVALID_FA_MAP_CLASS_VALUE = 4108;
- public static final int NAN_STATUS_INVALID_FA_MAP_CHANNEL_VALUE = 4109;
- public static final int NAN_STATUS_INVALID_FA_MAP_AVAILABILITY_INTERVAL_BITMAP_VALUE = 4110;
- public static final int NAN_STATUS_INVALID_FA_MAP_MAP_ID = 4111;
- public static final int NAN_STATUS_INVALID_POST_NAN_DISCOVERY_CONN_TYPE_VALUE = 4112;
- public static final int NAN_STATUS_INVALID_POST_NAN_DISCOVERY_DEVICE_ROLE_VALUE = 4113;
- public static final int NAN_STATUS_INVALID_POST_NAN_DISCOVERY_DURATION_VALUE = 4114;
- 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;
-
- // 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;
- public static final int NAN_TERMINATED_REASON_FAILURE = 8195;
- public static final int NAN_TERMINATED_REASON_COUNT_REACHED = 8196;
- public static final int NAN_TERMINATED_REASON_DE_SHUTDOWN = 8197;
- public static final int NAN_TERMINATED_REASON_DISABLE_IN_PROGRESS = 8198;
- public static final int NAN_TERMINATED_REASON_POST_DISC_ATTR_EXPIRED = 8199;
- 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) {
- 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_INVALID_RSSI_CLOSE_VALUE:
- case NAN_STATUS_INVALID_RSSI_MIDDLE_VALUE:
- case NAN_STATUS_INVALID_HOP_COUNT_LIMIT:
- case NAN_STATUS_INVALID_MASTER_PREFERENCE_VALUE:
- case NAN_STATUS_INVALID_LOW_CLUSTER_ID_VALUE:
- case NAN_STATUS_INVALID_HIGH_CLUSTER_ID_VALUE:
- case NAN_STATUS_INVALID_BACKGROUND_SCAN_PERIOD:
- case NAN_STATUS_INVALID_RSSI_PROXIMITY_VALUE:
- case NAN_STATUS_INVALID_SCAN_CHANNEL:
- case NAN_STATUS_INVALID_POST_NAN_CONNECTIVITY_CAPABILITIES_BITMAP:
- case NAN_STATUS_INVALID_FA_MAP_NUMCHAN_VALUE:
- case NAN_STATUS_INVALID_FA_MAP_DURATION_VALUE:
- case NAN_STATUS_INVALID_FA_MAP_CLASS_VALUE:
- case NAN_STATUS_INVALID_FA_MAP_CHANNEL_VALUE:
- case NAN_STATUS_INVALID_FA_MAP_AVAILABILITY_INTERVAL_BITMAP_VALUE:
- case NAN_STATUS_INVALID_FA_MAP_MAP_ID:
- case NAN_STATUS_INVALID_POST_NAN_DISCOVERY_CONN_TYPE_VALUE:
- case NAN_STATUS_INVALID_POST_NAN_DISCOVERY_DEVICE_ROLE_VALUE:
- case NAN_STATUS_INVALID_POST_NAN_DISCOVERY_DURATION_VALUE:
- 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;
-
- // publish/subscribe termination reasons
- case NAN_TERMINATED_REASON_TIMEOUT:
- case NAN_TERMINATED_REASON_USER_REQUEST:
- case NAN_TERMINATED_REASON_COUNT_REACHED:
- return WifiNanSessionListener.TERMINATE_REASON_DONE;
-
- case NAN_TERMINATED_REASON_INVALID:
- case NAN_TERMINATED_REASON_FAILURE:
- case NAN_TERMINATED_REASON_DE_SHUTDOWN:
- case NAN_TERMINATED_REASON_DISABLE_IN_PROGRESS:
- 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 WifiNanSessionListener.FAIL_REASON_OTHER;
- }
-
- // callback from native
- private static void onNanNotifyResponse(short transactionId, int responseType, int status,
- int value) {
- if (VDBG) {
- Log.v(TAG,
- "onNanNotifyResponse: transactionId=" + transactionId + ", responseType="
- + responseType + ", status=" + status + ", value=" + value);
- }
-
- switch (responseType) {
- case NAN_RESPONSE_ENABLED:
- if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onConfigCompleted(transactionId);
- } else {
- WifiNanStateManager.getInstance().onConfigFailed(transactionId,
- translateHalStatusToPublicStatus(status));
- }
- break;
- case NAN_RESPONSE_PUBLISH_CANCEL:
- if (status != NAN_STATUS_SUCCESS) {
- Log.e(TAG, "onNanNotifyResponse: NAN_RESPONSE_PUBLISH_CANCEL error - status="
- + status + ", value=" + value);
- }
- break;
- case NAN_RESPONSE_TRANSMIT_FOLLOWUP:
- if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onMessageSendSuccess(transactionId);
- } else {
- WifiNanStateManager.getInstance().onMessageSendFail(transactionId,
- translateHalStatusToPublicStatus(status));
- }
- break;
- case NAN_RESPONSE_SUBSCRIBE_CANCEL:
- if (status != NAN_STATUS_SUCCESS) {
- Log.e(TAG, "onNanNotifyResponse: NAN_RESPONSE_PUBLISH_CANCEL error - status="
- + status + ", value=" + value);
- }
- break;
- default:
- WifiNanStateManager.getInstance().onUnknownTransaction(responseType, transactionId,
- translateHalStatusToPublicStatus(status));
- break;
- }
- }
-
- private static void onNanNotifyResponsePublishSubscribe(short transactionId, int responseType,
- int status, int value, int pubSubId) {
- if (VDBG) {
- Log.v(TAG,
- "onNanNotifyResponsePublishSubscribe: transactionId=" + transactionId
- + ", responseType=" + responseType + ", status=" + status + ", value="
- + value + ", pubSubId=" + pubSubId);
- }
-
- switch (responseType) {
- case NAN_RESPONSE_PUBLISH:
- if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onPublishSuccess(transactionId, pubSubId);
- } else {
- WifiNanStateManager.getInstance().onPublishFail(transactionId,
- translateHalStatusToPublicStatus(status));
- }
- break;
- case NAN_RESPONSE_SUBSCRIBE:
- if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onSubscribeSuccess(transactionId, pubSubId);
- } else {
- WifiNanStateManager.getInstance().onSubscribeFail(transactionId,
- translateHalStatusToPublicStatus(status));
- }
- break;
- default:
- WifiNanStateManager.getInstance().onUnknownTransaction(responseType, transactionId,
- translateHalStatusToPublicStatus(status));
- break;
- }
- }
-
- private static void onNanNotifyResponseCapabilities(short transactionId, int status, int value,
- Capabilities capabilities) {
- if (VDBG) {
- Log.v(TAG, "onNanNotifyResponsePublishSubscribe: transactionId=" + transactionId
- + ", status=" + status + ", value=" + value + ", capabilities=" + capabilities);
- }
-
- if (status == NAN_STATUS_SUCCESS) {
- WifiNanStateManager.getInstance().onCapabilitiesUpdate(transactionId, capabilities);
- } else {
- Log.e(TAG,
- "onNanNotifyResponseCapabilities: error status=" + status + ", value=" + value);
- }
- }
-
- 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;
-
- // callback from native
- private static void onDiscoveryEngineEvent(int eventType, byte[] mac) {
- if (VDBG) {
- Log.v(TAG, "onDiscoveryEngineEvent: eventType=" + eventType + ", mac="
- + String.valueOf(HexEncoding.encode(mac)));
- }
-
- if (eventType == NAN_EVENT_ID_DISC_MAC_ADDR) {
- WifiNanStateManager.getInstance().onInterfaceAddressChange(mac);
- } else if (eventType == NAN_EVENT_ID_STARTED_CLUSTER) {
- WifiNanStateManager.getInstance()
- .onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, mac);
- } else if (eventType == NAN_EVENT_ID_JOINED_CLUSTER) {
- WifiNanStateManager.getInstance()
- .onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_JOINED, mac);
- } else {
- Log.w(TAG, "onDiscoveryEngineEvent: invalid eventType=" + eventType);
- }
- }
-
- // callback from native
- private static void onMatchEvent(int pubSubId, int requestorInstanceId, byte[] mac,
- byte[] serviceSpecificInfo, int serviceSpecificInfoLength, byte[] matchFilter,
- int matchFilterLength) {
- if (VDBG) {
- Log.v(TAG, "onMatchEvent: pubSubId=" + pubSubId + ", requestorInstanceId="
- + requestorInstanceId + ", mac=" + String.valueOf(HexEncoding.encode(mac))
- + ", serviceSpecificInfo=" + serviceSpecificInfo + ", matchFilterLength="
- + matchFilterLength + ", matchFilter=" + matchFilter);
- }
-
- WifiNanStateManager.getInstance().onMatch(pubSubId, requestorInstanceId, mac,
- serviceSpecificInfo, serviceSpecificInfoLength, matchFilter, matchFilterLength);
- }
-
- // callback from native
- private static void onPublishTerminated(int publishId, int status) {
- if (VDBG) Log.v(TAG, "onPublishTerminated: publishId=" + publishId + ", status=" + status);
-
- WifiNanStateManager.getInstance().onPublishTerminated(publishId,
- translateHalStatusToPublicStatus(status));
- }
-
- // callback from native
- private static void onSubscribeTerminated(int subscribeId, int status) {
- if (VDBG) {
- Log.v(TAG, "onSubscribeTerminated: subscribeId=" + subscribeId + ", status=" + status);
- }
-
- WifiNanStateManager.getInstance().onSubscribeTerminated(subscribeId,
- translateHalStatusToPublicStatus(status));
- }
-
- // callback from native
- private static void onFollowupEvent(int pubSubId, int requestorInstanceId, byte[] mac,
- byte[] message, int messageLength) {
- if (VDBG) {
- Log.v(TAG, "onFollowupEvent: pubSubId=" + pubSubId + ", requestorInstanceId="
- + requestorInstanceId + ", mac=" + String.valueOf(HexEncoding.encode(mac))
- + ", messageLength=" + messageLength);
- }
-
- WifiNanStateManager.getInstance().onMessageReceived(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));
- }
-}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanService.java b/service/java/com/android/server/wifi/nan/WifiNanService.java
deleted file mode 100644
index b5920f2..0000000
--- a/service/java/com/android/server/wifi/nan/WifiNanService.java
+++ /dev/null
@@ -1,46 +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.nan;
-
-import android.content.Context;
-import android.util.Log;
-
-import com.android.server.SystemService;
-
-public final class WifiNanService extends SystemService {
- private static final String TAG = "WifiNanService";
- final WifiNanServiceImpl mImpl;
-
- public WifiNanService(Context context) {
- super(context);
- mImpl = new WifiNanServiceImpl(context);
- }
-
- @Override
- public void onStart() {
- Log.i(TAG, "Registering " + Context.WIFI_NAN_SERVICE);
- publishBinderService(Context.WIFI_NAN_SERVICE, mImpl);
- }
-
- @Override
- public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
- mImpl.start();
- }
- }
-}
-
diff --git a/service/java/com/android/server/wifi/nan/WifiNanServiceImpl.java b/service/java/com/android/server/wifi/nan/WifiNanServiceImpl.java
deleted file mode 100644
index deefe94..0000000
--- a/service/java/com/android/server/wifi/nan/WifiNanServiceImpl.java
+++ /dev/null
@@ -1,245 +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.nan;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.IWifiNanEventListener;
-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.os.Binder;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-public class WifiNanServiceImpl extends IWifiNanManager.Stub {
- private static final String TAG = "WifiNanService";
- private static final boolean DBG = false;
- private static final boolean VDBG = false; // STOPSHIP if true
-
- 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;
-
- 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();
- }
-
- public void start() {
- Log.i(TAG, "Starting Wi-Fi NAN service");
-
- // TODO: share worker thread with other Wi-Fi handlers
- HandlerThread wifiNanThread = new HandlerThread("wifiNanService");
- wifiNanThread.start();
-
- mStateManager.start(wifiNanThread.getLooper());
- }
-
- @Override
- public void connect(final IBinder binder, IWifiNanEventListener listener, int events) {
- enforceAccessPermission();
- enforceChangePermission();
-
- final int uid = getCallingUid();
-
- if (VDBG) Log.v(TAG, "connect: uid=" + uid);
-
-
- IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- if (DBG) Log.d(TAG, "binderDied: uid=" + uid);
- binder.unlinkToDeath(this, 0);
-
- synchronized (mLock) {
- mDeathRecipientsByUid.delete(uid);
- }
-
- mStateManager.disconnect(uid);
- }
- };
- synchronized (mLock) {
- mDeathRecipientsByUid.put(uid, dr);
- }
- try {
- binder.linkToDeath(dr, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Error on linkToDeath - " + e);
- }
-
- mStateManager.connect(uid, listener, events);
- }
-
- @Override
- public void disconnect(IBinder binder) {
- enforceAccessPermission();
- enforceChangePermission();
-
- int uid = getCallingUid();
-
- if (VDBG) Log.v(TAG, "disconnect: uid=" + uid);
-
- synchronized (mLock) {
- IBinder.DeathRecipient dr = mDeathRecipientsByUid.get(uid);
- if (dr != null) {
- binder.unlinkToDeath(dr, 0);
- mDeathRecipientsByUid.delete(uid);
- }
- }
-
- mStateManager.disconnect(uid);
- }
-
- @Override
- public void requestConfig(ConfigRequest configRequest) {
- enforceAccessPermission();
- enforceChangePermission();
-
- if (VDBG) {
- Log.v(TAG,
- "requestConfig: uid=" + getCallingUid() + ", configRequest=" + configRequest);
- }
-
- mStateManager.requestConfig(getCallingUid(), configRequest);
- }
-
- @Override
- public void stopSession(int sessionId) {
- enforceAccessPermission();
- enforceChangePermission();
-
- if (VDBG) Log.v(TAG, "stopSession: sessionId=" + sessionId + ", uid=" + getCallingUid());
-
- mStateManager.stopSession(getCallingUid(), sessionId);
- }
-
- @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;
- synchronized (mLock) {
- sessionId = mNextSessionId++;
- }
-
- 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);
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump WifiNanService from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
- return;
- }
- pw.println("Wi-Fi NAN Service");
- pw.println(" mNanSupported: " + mNanSupported);
- pw.println(" mNextNetworkRequestToken: " + mNextNetworkRequestToken);
- pw.println(" mNextSessionId: " + mNextSessionId);
- pw.println(" mDeathRecipientsByUid: " + mDeathRecipientsByUid);
- mStateManager.dump(fd, pw, args);
- }
-
- private void enforceAccessPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG);
- }
-
- private void enforceChangePermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG);
- }
-}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanSessionState.java b/service/java/com/android/server/wifi/nan/WifiNanSessionState.java
deleted file mode 100644
index ea64403..0000000
--- a/service/java/com/android/server/wifi/nan/WifiNanSessionState.java
+++ /dev/null
@@ -1,255 +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.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.os.RemoteException;
-import android.util.Log;
-import android.util.SparseArray;
-
-import libcore.util.HexEncoding;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-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 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) {
- mSessionId = sessionId;
- mListener = listener;
- mEvents = events;
- }
-
- public void destroy() {
- stop(WifiNanStateManager.getInstance().createNextTransactionId());
- if (mPubSubIdValid) {
- mMacByRequestorInstanceId.clear();
- mListener = null;
- mPubSubIdValid = false;
- }
- }
-
- public int getSessionId() {
- return mSessionId;
- }
-
- public boolean isPubSubIdSession(int pubSubId) {
- return mPubSubIdValid && 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");
- }
- mSessionType = SESSION_TYPE_PUBLISH;
-
- WifiNanNative.getInstance().publish(transactionId, mPubSubIdValid ? mPubSubId : 0, data,
- settings);
- }
-
- 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");
- }
- mSessionType = SESSION_TYPE_SUBSCRIBE;
-
- WifiNanNative.getInstance().subscribe(transactionId, mPubSubIdValid ? mPubSubId : 0, data,
- settings);
- }
-
- public void 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;
- }
- byte[] peerMac = HexEncoding.decode(peerMacStr.toCharArray(), false);
-
- WifiNanNative.getInstance().sendMessage(transactionId, mPubSubId, peerId, peerMac, message,
- messageLength);
- }
-
- 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);
- }
- }
-
- public void onMatch(int requestorInstanceId, byte[] peerMac, byte[] serviceSpecificInfo,
- int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
- String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
- mMacByRequestorInstanceId.put(requestorInstanceId, new String(HexEncoding.encode(peerMac)));
-
- 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);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "onMatch: RemoteException (FYI): " + e);
- }
- }
-
- public void onMessageReceived(int requestorInstanceId, byte[] peerMac, byte[] message,
- int messageLength) {
- String prevMac = mMacByRequestorInstanceId.get(requestorInstanceId);
- mMacByRequestorInstanceId.put(requestorInstanceId, new String(HexEncoding.encode(peerMac)));
-
- if (DBG) {
- Log.d(TAG, "onMessageReceived: previous peer MAC replaced - " + prevMac);
- }
-
- try {
- if (mListener != null
- && (mEvents & WifiNanSessionListener.LISTEN_MESSAGE_RECEIVED) != 0) {
- mListener.onMessageReceived(requestorInstanceId, message, messageLength);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "onMessageReceived: RemoteException (FYI): " + e);
- }
- }
-
- 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(" mMacByRequestorInstanceId: [" + mMacByRequestorInstanceId + "]");
- }
-}
diff --git a/service/java/com/android/server/wifi/nan/WifiNanStateManager.java b/service/java/com/android/server/wifi/nan/WifiNanStateManager.java
deleted file mode 100644
index f7bfa55..0000000
--- a/service/java/com/android/server/wifi/nan/WifiNanStateManager.java
+++ /dev/null
@@ -1,1172 +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.nan;
-
-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.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-import android.util.SparseArray;
-
-import libcore.util.HexEncoding;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-public class WifiNanStateManager {
- private static final String TAG = "WifiNanStateManager";
- private static final boolean DBG = false;
- private static final boolean VDBG = false; // STOPSHIP if true
-
- 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;
-
- 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_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";
- private static final String MESSAGE_BUNDLE_KEY_FILTER_DATA = "filter_data";
- 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 WifiNanNative.Capabilities mCapabilities;
-
- private WifiNanStateHandler mHandler;
-
- // 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 WifiNanStateManager() {
- // EMPTY: singleton pattern
- }
-
- public static WifiNanStateManager getInstance() {
- if (sNanStateManagerSingleton == null) {
- sNanStateManagerSingleton = new WifiNanStateManager();
- }
-
- return sNanStateManagerSingleton;
- }
-
- public void start(Looper looper) {
- Log.i(TAG, "start()");
-
- mHandler = new WifiNanStateHandler(looper);
- }
-
- 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);
- }
-
- public void disconnect(int uid) {
- Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT);
- msg.arg1 = uid;
- mHandler.sendMessage(msg);
- }
-
- public void requestConfig(int uid, ConfigRequest configRequest) {
- Message msg = mHandler.obtainMessage(MESSAGE_REQUEST_CONFIG);
- msg.arg1 = uid;
- msg.obj = configRequest;
- mHandler.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);
- }
-
- public void destroySession(int uid, int sessionId) {
- Message msg = mHandler.obtainMessage(MESSAGE_DESTROY_SESSION);
- msg.arg1 = uid;
- msg.arg2 = sessionId;
- mHandler.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);
- }
-
- 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);
- }
-
- 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);
- }
-
- 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);
- }
-
- public void onCapabilitiesUpdate(short transactionId, WifiNanNative.Capabilities capabilities) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_CAPABILITIES_UPDATED);
- msg.arg1 = transactionId;
- msg.obj = capabilities;
- mHandler.sendMessage(msg);
- }
-
- public void onConfigCompleted(short transactionId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_CONFIG_COMPLETED);
- msg.arg1 = transactionId;
- mHandler.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);
- }
-
- public void onPublishSuccess(short transactionId, int publishId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_PUBLISH_SUCCESS);
- msg.arg1 = transactionId;
- msg.arg2 = publishId;
- mHandler.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);
- }
-
- public void onMessageSendSuccess(short transactionId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_MESSAGE_SEND_SUCCESS);
- msg.arg1 = transactionId;
- mHandler.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);
- }
-
- public void onSubscribeSuccess(short transactionId, int subscribeId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_SUBSCRIBE_SUCCESS);
- msg.arg1 = transactionId;
- msg.arg2 = subscribeId;
- mHandler.sendMessage(msg);
- }
-
- 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);
- msg.obj = mac;
- mHandler.sendMessage(msg);
- }
-
- public void onClusterChange(int flag, byte[] clusterId) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_CLUSTER_CHANGE);
- msg.arg1 = flag;
- msg.obj = clusterId;
- mHandler.sendMessage(msg);
- }
-
- public void onMatch(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);
- }
-
- public void onPublishTerminated(int publishId, int status) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_PUBLISH_TERMINATED);
- msg.arg1 = publishId;
- msg.arg2 = status;
- mHandler.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,
- 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);
- }
-
- public void onNanDown(int reason) {
- Message msg = mHandler.obtainMessage(MESSAGE_ON_NAN_DOWN);
- msg.arg1 = reason;
- mHandler.sendMessage(msg);
- }
-
- private class WifiNanStateHandler extends Handler {
- WifiNanStateHandler(android.os.Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (DBG) {
- Log.d(TAG, "Message: " + msg.what);
- }
- 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;
- }
- 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;
- }
- 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);
- }
-
- sendFollowonMessageLocal(msg.arg1, sessionId, peerId, message, msg.arg2,
- messageId);
- break;
- }
- case MESSAGE_STOP_SESSION: {
- if (VDBG) {
- Log.d(TAG, "Stop session");
- }
- stopSessionLocal(msg.arg1, msg.arg2);
- 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;
- byte[] peerMac = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS);
- byte[] serviceSpecificInfo = msg.getData()
- .getByteArray(MESSAGE_BUNDLE_KEY_SSI_DATA);
- int serviceSpecificInfoLength = msg.getData()
- .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;
- 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;
- }
- default:
- Log.e(TAG, "Unknown message code: " + msg.what);
- }
- }
- }
-
- /*
- * Transaction management classes & operations
- */
-
- // 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) {
- if (VDBG) {
- Log.v(TAG, "connect(): uid=" + uid + ", listener=" + listener + ", events=" + events);
- }
-
- if (mClients.get(uid) != null) {
- Log.e(TAG, "connect: entry already exists for uid=" + uid);
- return;
- }
-
- WifiNanClientState client = new WifiNanClientState(uid, listener, events);
- mClients.put(uid, client);
- }
-
- private void disconnectLocal(int uid) {
- if (VDBG) {
- Log.v(TAG, "disconnect(): uid=" + uid);
- }
-
- WifiNanClientState client = mClients.get(uid);
- mClients.delete(uid);
-
- if (client == null) {
- Log.e(TAG, "disconnect: no entry for uid=" + uid);
- return;
- }
-
- 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);
- }
-
- client.destroy();
-
- if (mClients.size() == 0) {
- WifiNanNative.getInstance().disable(createTransactionInfo().mTransactionId);
- return;
- }
-
- ConfigRequest merged = mergeConfigRequests();
-
- WifiNanNative.getInstance()
- .enableAndConfigure(createTransactionInfoConfig(merged).mTransactionId, merged);
- }
-
- private void requestConfigLocal(int uid, ConfigRequest configRequest) {
- if (VDBG) {
- Log.v(TAG, "requestConfig(): uid=" + uid + ", configRequest=" + configRequest);
- }
-
- WifiNanClientState client = mClients.get(uid);
- if (client == null) {
- Log.e(TAG, "requestConfig: no client exists for uid=" + uid);
- return;
- }
-
- client.setConfigRequest(configRequest);
-
- ConfigRequest merged = mergeConfigRequests();
-
- WifiNanNative.getInstance()
- .enableAndConfigure(createTransactionInfoConfig(merged).mTransactionId, merged);
- }
-
- private void createSessionLocal(int uid, int sessionId, IWifiNanSessionListener listener,
- int events) {
- if (VDBG) {
- Log.v(TAG, "createSession(): uid=" + uid + ", sessionId=" + sessionId + ", listener="
- + listener + ", events=" + events);
- }
-
- WifiNanClientState client = mClients.get(uid);
- if (client == null) {
- Log.e(TAG, "createSession: no client exists for uid=" + uid);
- return;
- }
-
- client.createSession(sessionId, listener, events);
- }
-
- private void destroySessionLocal(int uid, int sessionId) {
- if (VDBG) {
- Log.v(TAG, "destroySession(): uid=" + uid + ", sessionId=" + sessionId);
- }
-
- WifiNanClientState client = mClients.get(uid);
- if (client == null) {
- Log.e(TAG, "destroySession: no client exists for uid=" + uid);
- 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);
- }
- }
- for (Integer id : toRemove) {
- mPendingResponses.removeAt(id);
- }
-
- client.destroySession(sessionId);
- }
-
- private void publishLocal(int uid, int sessionId, PublishData publishData,
- PublishSettings publishSettings) {
- if (VDBG) {
- Log.v(TAG, "publish(): uid=" + uid + ", sessionId=" + sessionId + ", data="
- + publishData + ", settings=" + publishSettings);
- }
-
- TransactionInfoSession info = createTransactionInfoSession(uid, sessionId);
-
- info.mSession.publish(info.mTransactionId, publishData, publishSettings);
- }
-
- private void subscribeLocal(int uid, int sessionId, SubscribeData subscribeData,
- SubscribeSettings subscribeSettings) {
- if (VDBG) {
- Log.v(TAG, "subscribe(): uid=" + uid + ", sessionId=" + sessionId + ", data="
- + subscribeData + ", settings=" + subscribeSettings);
- }
-
- TransactionInfoSession info = createTransactionInfoSession(uid, sessionId);
-
- info.mSession.subscribe(info.mTransactionId, subscribeData, subscribeSettings);
- }
-
- private void sendFollowonMessageLocal(int uid, int sessionId, int peerId, byte[] message,
- int messageLength, int messageId) {
- if (VDBG) {
- Log.v(TAG, "sendMessage(): uid=" + uid + ", sessionId=" + sessionId + ", peerId="
- + peerId + ", messageLength=" + messageLength + ", messageId=" + messageId);
- }
-
- 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);
- }
-
- /*
- * Callbacks (calls from HAL/Native to service)
- */
-
- private void onCapabilitiesUpdatedLocal(short transactionId,
- WifiNanNative.Capabilities capabilities) {
- if (VDBG) {
- Log.v(TAG, "onCapabilitiesUpdatedLocal: transactionId=" + transactionId
- + ", capabilites=" + capabilities);
- }
-
- mCapabilities = capabilities;
- }
-
- private void onConfigCompletedLocal(short transactionId) {
- if (VDBG) {
- Log.v(TAG, "onConfigCompleted: transactionId=" + transactionId);
- }
-
- 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);
- }
- }
-
- private void onConfigFailedLocal(short transactionId, int reason) {
- if (VDBG) {
- Log.v(TAG, "onEnableFailed: transactionId=" + transactionId + ", reason=" + reason);
- }
-
- 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);
- }
- }
-
- private void onNanDownLocal(int reason) {
- if (VDBG) {
- Log.v(TAG, "onNanDown: reason=" + reason);
- }
-
- 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!");
- }
- }
-
- private void onInterfaceAddressChangeLocal(byte[] mac) {
- if (VDBG) {
- Log.v(TAG, "onInterfaceAddressChange: mac=" + String.valueOf(HexEncoding.encode(mac)));
- }
-
- int interested = 0;
- 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!");
- }
- }
-
- private void onClusterChangeLocal(int flag, byte[] clusterId) {
- if (VDBG) {
- Log.v(TAG, "onClusterChange: flag=" + flag + ", clusterId="
- + 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);
- }
- }
-
- private void onMatchLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
- byte[] serviceSpecificInfo, int serviceSpecificInfoLength, byte[] matchFilter,
- int matchFilterLength) {
- if (VDBG) {
- Log.v(TAG, "onMatch: pubSubId=" + pubSubId + ", requestorInstanceId="
- + requestorInstanceId + ", peerMac="
- + String.valueOf(HexEncoding.encode(peerMac)) + ", serviceSpecificInfoLength="
- + serviceSpecificInfoLength + ", serviceSpecificInfo=" + serviceSpecificInfo
- + ", matchFilterLength=" + matchFilterLength + ", matchFilter=" + matchFilter);
- }
-
- WifiNanSessionState session = getNanSessionStateForPubSubId(pubSubId);
- if (session == null) {
- Log.e(TAG, "onMatch: no session found for pubSubId=" + pubSubId);
- return;
- }
-
- session.onMatch(requestorInstanceId, peerMac, serviceSpecificInfo,
- serviceSpecificInfoLength, matchFilter, matchFilterLength);
- }
-
- private void onMessageReceivedLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
- byte[] message, int messageLength) {
- if (VDBG) {
- Log.v(TAG,
- "onMessageReceived: pubSubId=" + pubSubId + ", requestorInstanceId="
- + requestorInstanceId + ", peerMac="
- + String.valueOf(HexEncoding.encode(peerMac)) + ", messageLength="
- + messageLength);
- }
-
- WifiNanSessionState session = getNanSessionStateForPubSubId(pubSubId);
- if (session == null) {
- Log.e(TAG, "onMessageReceived: no session found for pubSubId=" + pubSubId);
- return;
- }
-
- session.onMessageReceived(requestorInstanceId, peerMac, message, messageLength);
- }
-
- private ConfigRequest mergeConfigRequests() {
- if (VDBG) {
- Log.v(TAG, "mergeConfigRequests(): mClients=[" + mClients + "]");
- }
-
- if (mClients.size() == 0) {
- 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
- boolean support5gBand = false;
- int masterPreference = 0;
- boolean clusterIdValid = false;
- int clusterLow = 0;
- int clusterHigh = ConfigRequest.CLUSTER_ID_MAX;
- for (int i = 0; i < mClients.size(); ++i) {
- ConfigRequest cr = mClients.valueAt(i).getConfigRequest();
-
- if (cr.mSupport5gBand) {
- support5gBand = true;
- }
-
- masterPreference = Math.max(masterPreference, cr.mMasterPreference);
-
- if (cr.mClusterLow != 0 || cr.mClusterHigh != ConfigRequest.CLUSTER_ID_MAX) {
- if (!clusterIdValid) {
- clusterLow = cr.mClusterLow;
- clusterHigh = cr.mClusterHigh;
- } else {
- clusterLow = Math.min(clusterLow, cr.mClusterLow);
- clusterHigh = Math.max(clusterHigh, cr.mClusterHigh);
- }
- clusterIdValid = true;
- }
- }
- ConfigRequest.Builder builder = new ConfigRequest.Builder();
- builder.setSupport5gBand(support5gBand).setMasterPreference(masterPreference)
- .setClusterLow(clusterLow).setClusterHigh(clusterHigh);
-
- return builder.build();
- }
-
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("NanStateManager:");
- pw.println(" mClients: [" + mClients + "]");
- pw.println(" mPendingResponses: [" + mPendingResponses + "]");
- pw.println(" mCapabilities: [" + mCapabilities + "]");
- pw.println(" mNextTransactionId: " + mNextTransactionId);
- for (int i = 0; i < mClients.size(); ++i) {
- mClients.valueAt(i).dump(fd, pw, args);
- }
- }
-}
diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallback.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallback.java
new file mode 100644
index 0000000..802f643
--- /dev/null
+++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallback.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.p2p;
+
+import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback;
+import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
+import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pProvDiscEvent;
+import android.net.wifi.p2p.WifiP2pWfdInfo;
+import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
+import android.util.Log;
+
+import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus;
+import com.android.server.wifi.util.NativeUtil;
+
+import libcore.util.HexEncoding;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Class used for processing all P2P callbacks.
+ */
+public class SupplicantP2pIfaceCallback extends ISupplicantP2pIfaceCallback.Stub {
+ private static final String TAG = "SupplicantP2pIfaceCallback";
+ private static final boolean DBG = true;
+
+ private final String mInterface;
+ private final WifiP2pMonitor mMonitor;
+
+ public SupplicantP2pIfaceCallback(String iface, WifiP2pMonitor monitor) {
+ mInterface = iface;
+ mMonitor = monitor;
+ }
+
+
+ protected static void logd(String s) {
+ if (DBG) Log.d(TAG, s);
+ }
+
+ /**
+ * Used to indicate that a new network has been added.
+ *
+ * @param networkId Network ID allocated to the corresponding network.
+ */
+ public void onNetworkAdded(int networkId) {
+ }
+
+
+ /**
+ * Used to indicate that a network has been removed.
+ *
+ * @param networkId Network ID allocated to the corresponding network.
+ */
+ public void onNetworkRemoved(int networkId) {
+ }
+
+
+ /**
+ * Used to indicate that a P2P device has been found.
+ *
+ * @param srcAddress MAC address of the device found. This must either
+ * be the P2P device address or the P2P interface address.
+ * @param p2pDeviceAddress P2P device address.
+ * @param primaryDeviceType Type of device. Refer to section B.1 of Wifi P2P
+ * Technical specification v1.2.
+ * @param deviceName Name of the device.
+ * @param configMethods Mask of WPS configuration methods supported by the
+ * device.
+ * @param deviceCapabilities Refer to section 4.1.4 of Wifi P2P Technical
+ * specification v1.2.
+ * @param groupCapabilities Refer to section 4.1.4 of Wifi P2P Technical
+ * specification v1.2.
+ * @param wfdDeviceInfo WFD device info as described in section 5.1.2 of WFD
+ * technical specification v1.0.0.
+ */
+ public void onDeviceFound(byte[] srcAddress, byte[] p2pDeviceAddress, byte[] primaryDeviceType,
+ String deviceName, short configMethods, byte deviceCapabilities, int groupCapabilities,
+ byte[] wfdDeviceInfo) {
+ WifiP2pDevice device = new WifiP2pDevice();
+ device.deviceName = deviceName;
+
+ if (deviceName == null) {
+ Log.e(TAG, "Missing device name.");
+ return;
+ }
+
+ try {
+ device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not decode device address.", e);
+ return;
+ }
+
+ try {
+ device.primaryDeviceType = new String(HexEncoding.encode(
+ primaryDeviceType, 0, primaryDeviceType.length));
+ } catch (Exception e) {
+ Log.e(TAG, "Could not encode device primary type.", e);
+ return;
+ }
+
+ device.deviceCapability = deviceCapabilities;
+ device.groupCapability = groupCapabilities;
+ device.wpsConfigMethodsSupported = configMethods;
+ device.status = WifiP2pDevice.AVAILABLE;
+
+ if (wfdDeviceInfo != null && wfdDeviceInfo.length >= 6) {
+ device.wfdInfo = new WifiP2pWfdInfo(
+ (wfdDeviceInfo[0] << 8) + wfdDeviceInfo[1],
+ (wfdDeviceInfo[2] << 8) + wfdDeviceInfo[3],
+ (wfdDeviceInfo[4] << 8) + wfdDeviceInfo[5]);
+ }
+
+ logd("Device discovered on " + mInterface + ": " + device);
+ mMonitor.broadcastP2pDeviceFound(mInterface, device);
+ }
+
+
+ /**
+ * Used to indicate that a P2P device has been lost.
+ *
+ * @param p2pDeviceAddress P2P device address.
+ */
+ public void onDeviceLost(byte[] p2pDeviceAddress) {
+ WifiP2pDevice device = new WifiP2pDevice();
+
+ try {
+ device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not decode device address.", e);
+ return;
+ }
+
+ device.status = WifiP2pDevice.UNAVAILABLE;
+
+ logd("Device lost on " + mInterface + ": " + device);
+ mMonitor.broadcastP2pDeviceLost(mInterface, device);
+ }
+
+
+ /**
+ * Used to indicate the termination of P2P find operation.
+ */
+ public void onFindStopped() {
+ logd("Search stopped on " + mInterface);
+ mMonitor.broadcastP2pFindStopped(mInterface);
+ }
+
+
+ /**
+ * Used to indicate the reception of a P2P Group Owner negotiation request.
+ *
+ * @param srcAddress MAC address of the device that initiated the GO
+ * negotiation request.
+ * @param passwordId Type of password.
+ */
+ public void onGoNegotiationRequest(byte[] srcAddress, short passwordId) {
+ WifiP2pConfig config = new WifiP2pConfig();
+
+ try {
+ config.deviceAddress = NativeUtil.macAddressFromByteArray(srcAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not decode device address.", e);
+ return;
+ }
+
+ config.wps = new WpsInfo();
+
+ switch (passwordId) {
+ case WpsDevPasswordId.USER_SPECIFIED:
+ config.wps.setup = WpsInfo.DISPLAY;
+ break;
+
+ case WpsDevPasswordId.PUSHBUTTON:
+ config.wps.setup = WpsInfo.PBC;
+ break;
+
+ case WpsDevPasswordId.REGISTRAR_SPECIFIED:
+ config.wps.setup = WpsInfo.KEYPAD;
+ break;
+
+ default:
+ config.wps.setup = WpsInfo.PBC;
+ break;
+ }
+
+ logd("Group Owner negotiation initiated on " + mInterface + ": " + config);
+ mMonitor.broadcastP2pGoNegotiationRequest(mInterface, config);
+ }
+
+
+ /**
+ * Used to indicate the completion of a P2P Group Owner negotiation request.
+ *
+ * @param status Status of the GO negotiation.
+ */
+ public void onGoNegotiationCompleted(int status) {
+ logd("Group Owner negotiation completed with status: " + status);
+ P2pStatus result = halStatusToP2pStatus(status);
+
+ if (result == P2pStatus.SUCCESS) {
+ mMonitor.broadcastP2pGoNegotiationSuccess(mInterface);
+ } else {
+ mMonitor.broadcastP2pGoNegotiationFailure(mInterface, result);
+ }
+ }
+
+
+ /**
+ * Used to indicate a successful formation of a P2P group.
+ */
+ public void onGroupFormationSuccess() {
+ logd("Group formation successful on " + mInterface);
+ mMonitor.broadcastP2pGroupFormationSuccess(mInterface);
+ }
+
+
+ /**
+ * Used to indicate a failure to form a P2P group.
+ *
+ * @param failureReason Failure reason string for debug purposes.
+ */
+ public void onGroupFormationFailure(String failureReason) {
+ // TODO(ender): failureReason should probably be an int (P2pStatusCode).
+ logd("Group formation failed on " + mInterface + ": " + failureReason);
+ mMonitor.broadcastP2pGroupFormationFailure(mInterface, failureReason);
+ }
+
+
+ /**
+ * Used to indicate the start of a P2P group.
+ *
+ * @param groupIfName Interface name of the group. (For ex: p2p-p2p0-1)
+ * @param isGo Whether this device is owner of the group.
+ * @param ssid SSID of the group.
+ * @param frequency Frequency on which this group is created.
+ * @param psk PSK used to secure the group.
+ * @param passphrase PSK passphrase used to secure the group.
+ * @param goDeviceAddress MAC Address of the owner of this group.
+ * @param isPersistent Whether this group is persisted or not.
+ */
+ public void onGroupStarted(String groupIfName, boolean isGo, ArrayList<Byte> ssid,
+ int frequency, byte[] psk, String passphrase, byte[] goDeviceAddress,
+ boolean isPersistent) {
+ if (groupIfName == null) {
+ Log.e(TAG, "Missing group interface name.");
+ return;
+ }
+
+ logd("Group " + groupIfName + " started on " + mInterface);
+
+ WifiP2pGroup group = new WifiP2pGroup();
+ group.setInterface(groupIfName);
+
+ try {
+ String quotedSsid = NativeUtil.encodeSsid(ssid);
+ group.setNetworkName(NativeUtil.removeEnclosingQuotes(quotedSsid));
+ } catch (Exception e) {
+ Log.e(TAG, "Could not encode SSID.", e);
+ return;
+ }
+
+ group.setIsGroupOwner(isGo);
+ group.setPassphrase(passphrase);
+
+ if (isPersistent) {
+ group.setNetworkId(WifiP2pGroup.PERSISTENT_NET_ID);
+ } else {
+ group.setNetworkId(WifiP2pGroup.TEMPORARY_NET_ID);
+ }
+
+ WifiP2pDevice owner = new WifiP2pDevice();
+
+ try {
+ owner.deviceAddress = NativeUtil.macAddressFromByteArray(goDeviceAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not decode Group Owner address.", e);
+ return;
+ }
+
+ group.setOwner(owner);
+ mMonitor.broadcastP2pGroupStarted(mInterface, group);
+ }
+
+
+ /**
+ * Used to indicate the removal of a P2P group.
+ *
+ * @param groupIfName Interface name of the group. (For ex: p2p-p2p0-1)
+ * @param isGo Whether this device is owner of the group.
+ */
+ public void onGroupRemoved(String groupIfName, boolean isGo) {
+ if (groupIfName == null) {
+ Log.e(TAG, "Missing group name.");
+ return;
+ }
+
+ logd("Group " + groupIfName + " removed from " + mInterface);
+ WifiP2pGroup group = new WifiP2pGroup();
+ group.setInterface(groupIfName);
+ group.setIsGroupOwner(isGo);
+ mMonitor.broadcastP2pGroupRemoved(mInterface, group);
+ }
+
+
+ /**
+ * Used to indicate the reception of a P2P invitation.
+ *
+ * @param srcAddress MAC address of the device that sent the invitation.
+ * @param goDeviceAddress MAC Address of the owner of this group.
+ * @param bssid Bssid of the group.
+ * @param persistentNetworkId Persistent network Id of the group.
+ * @param operatingFrequency Frequency on which the invitation was received.
+ */
+ public void onInvitationReceived(byte[] srcAddress, byte[] goDeviceAddress,
+ byte[] bssid, int persistentNetworkId, int operatingFrequency) {
+ WifiP2pGroup group = new WifiP2pGroup();
+ group.setNetworkId(persistentNetworkId);
+
+ WifiP2pDevice client = new WifiP2pDevice();
+
+ try {
+ client.deviceAddress = NativeUtil.macAddressFromByteArray(srcAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not decode MAC address.", e);
+ return;
+ }
+
+ group.addClient(client);
+
+ WifiP2pDevice owner = new WifiP2pDevice();
+
+ try {
+ owner.deviceAddress = NativeUtil.macAddressFromByteArray(goDeviceAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not decode Group Owner MAC address.", e);
+ return;
+ }
+
+ group.setOwner(owner);
+
+ logd("Invitation received on " + mInterface + ": " + group);
+ mMonitor.broadcastP2pInvitationReceived(mInterface, group);
+ }
+
+
+ /**
+ * Used to indicate the result of the P2P invitation request.
+ *
+ * @param bssid Bssid of the group.
+ * @param status Status of the invitation.
+ */
+ public void onInvitationResult(byte[] bssid, int status) {
+ logd("Invitation completed with status: " + status);
+ mMonitor.broadcastP2pInvitationResult(mInterface, halStatusToP2pStatus(status));
+ }
+
+
+ /**
+ * Used to indicate the completion of a P2P provision discovery request.
+ *
+ * @param p2pDeviceAddress P2P device address.
+ * @param isRequest Whether we received or sent the provision discovery.
+ * @param status Status of the provision discovery (SupplicantStatusCode).
+ * @param configMethods Mask of WPS configuration methods supported.
+ * Only one configMethod bit should be set per call.
+ * @param generatedPin 8 digit pin generated.
+ */
+ public void onProvisionDiscoveryCompleted(byte[] p2pDeviceAddress, boolean isRequest,
+ byte status, short configMethods, String generatedPin) {
+ if (status != ISupplicantP2pIfaceCallback.P2pProvDiscStatusCode.SUCCESS) {
+ Log.e(TAG, "Provision discovery failed: " + status);
+ mMonitor.broadcastP2pProvisionDiscoveryFailure(mInterface);
+ return;
+ }
+
+ logd("Provision discovery " + (isRequest ? "request" : "response")
+ + " for WPS Config method: " + configMethods);
+
+ WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent();
+ event.device = new WifiP2pDevice();
+
+ try {
+ event.device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not decode MAC address.", e);
+ return;
+ }
+
+ if ((configMethods & WpsConfigMethods.PUSHBUTTON) != 0) {
+ if (isRequest) {
+ event.event = WifiP2pProvDiscEvent.PBC_REQ;
+ mMonitor.broadcastP2pProvisionDiscoveryPbcRequest(mInterface, event);
+ } else {
+ event.event = WifiP2pProvDiscEvent.PBC_RSP;
+ mMonitor.broadcastP2pProvisionDiscoveryPbcResponse(mInterface, event);
+ }
+ } else if (!isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) {
+ event.event = WifiP2pProvDiscEvent.SHOW_PIN;
+ event.pin = generatedPin;
+ mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event);
+ } else if (!isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) {
+ event.event = WifiP2pProvDiscEvent.ENTER_PIN;
+ mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event);
+ } else if (isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) {
+ event.event = WifiP2pProvDiscEvent.SHOW_PIN;
+ event.pin = generatedPin;
+ mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event);
+ } else if (isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) {
+ event.event = WifiP2pProvDiscEvent.ENTER_PIN;
+ mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event);
+ } else {
+ Log.e(TAG, "Unsupported config methods: " + configMethods);
+ }
+ }
+
+
+ /**
+ * Used to indicate the reception of a P2P service discovery response.
+ *
+ * @param srcAddress MAC address of the device that sent the service discovery.
+ * @param updateIndicator Service update indicator. Refer to section 3.1.3 of
+ * Wifi P2P Technical specification v1.2.
+ * @param tlvs Refer to section 3.1.3.1 of Wifi P2P Technical specification v1.2.
+ */
+ public void onServiceDiscoveryResponse(byte[] srcAddress, short updateIndicator,
+ ArrayList<Byte> tlvs) {
+ List<WifiP2pServiceResponse> response = null;
+
+ logd("Service discovery response received on " + mInterface);
+ try {
+ String srcAddressStr = NativeUtil.macAddressFromByteArray(srcAddress);
+ // updateIndicator is not used
+ response = WifiP2pServiceResponse.newInstance(srcAddressStr,
+ NativeUtil.byteArrayFromArrayList(tlvs));
+ } catch (Exception e) {
+ Log.e(TAG, "Could not process service discovery response.", e);
+ return;
+ }
+ mMonitor.broadcastP2pServiceDiscoveryResponse(mInterface, response);
+ }
+
+ private WifiP2pDevice createStaEventDevice(byte[] srcAddress, byte[] p2pDeviceAddress) {
+ WifiP2pDevice device = new WifiP2pDevice();
+ byte[] deviceAddressBytes;
+ // Legacy STAs may not supply a p2pDeviceAddress (signaled by a zero'd p2pDeviceAddress)
+ // In this case, use srcAddress instead
+ if (!Arrays.equals(NativeUtil.ANY_MAC_BYTES, p2pDeviceAddress)) {
+ deviceAddressBytes = p2pDeviceAddress;
+ } else {
+ deviceAddressBytes = srcAddress;
+ }
+ try {
+ device.deviceAddress = NativeUtil.macAddressFromByteArray(deviceAddressBytes);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not decode MAC address", e);
+ return null;
+ }
+ return device;
+ }
+
+ /**
+ * Used to indicate when a STA device is connected to this device.
+ *
+ * @param srcAddress MAC address of the device that was authorized.
+ * @param p2pDeviceAddress P2P device address.
+ */
+ public void onStaAuthorized(byte[] srcAddress, byte[] p2pDeviceAddress) {
+ logd("STA authorized on " + mInterface);
+ WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress);
+ if (device == null) {
+ return;
+ }
+ mMonitor.broadcastP2pApStaConnected(mInterface, device);
+ }
+
+
+ /**
+ * Used to indicate when a STA device is disconnected from this device.
+ *
+ * @param srcAddress MAC address of the device that was deauthorized.
+ * @param p2pDeviceAddress P2P device address.
+ */
+ public void onStaDeauthorized(byte[] srcAddress, byte[] p2pDeviceAddress) {
+ logd("STA deauthorized on " + mInterface);
+ WifiP2pDevice device = createStaEventDevice(srcAddress, p2pDeviceAddress);
+ if (device == null) {
+ return;
+ }
+ mMonitor.broadcastP2pApStaDisconnected(mInterface, device);
+ }
+
+
+ private static P2pStatus halStatusToP2pStatus(int status) {
+ P2pStatus result = P2pStatus.UNKNOWN;
+
+ switch (status) {
+ case P2pStatusCode.SUCCESS:
+ case P2pStatusCode.SUCCESS_DEFERRED:
+ result = P2pStatus.SUCCESS;
+ break;
+
+ case P2pStatusCode.FAIL_INFO_CURRENTLY_UNAVAILABLE:
+ result = P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE;
+ break;
+
+ case P2pStatusCode.FAIL_INCOMPATIBLE_PARAMS:
+ result = P2pStatus.INCOMPATIBLE_PARAMETERS;
+ break;
+
+ case P2pStatusCode.FAIL_LIMIT_REACHED:
+ result = P2pStatus.LIMIT_REACHED;
+ break;
+
+ case P2pStatusCode.FAIL_INVALID_PARAMS:
+ result = P2pStatus.INVALID_PARAMETER;
+ break;
+
+ case P2pStatusCode.FAIL_UNABLE_TO_ACCOMMODATE:
+ result = P2pStatus.UNABLE_TO_ACCOMMODATE_REQUEST;
+ break;
+
+ case P2pStatusCode.FAIL_PREV_PROTOCOL_ERROR:
+ result = P2pStatus.PREVIOUS_PROTOCOL_ERROR;
+ break;
+
+ case P2pStatusCode.FAIL_NO_COMMON_CHANNELS:
+ result = P2pStatus.NO_COMMON_CHANNEL;
+ break;
+
+ case P2pStatusCode.FAIL_UNKNOWN_GROUP:
+ result = P2pStatus.UNKNOWN_P2P_GROUP;
+ break;
+
+ case P2pStatusCode.FAIL_BOTH_GO_INTENT_15:
+ result = P2pStatus.BOTH_GO_INTENT_15;
+ break;
+
+ case P2pStatusCode.FAIL_INCOMPATIBLE_PROV_METHOD:
+ result = P2pStatus.INCOMPATIBLE_PROVISIONING_METHOD;
+ break;
+
+ case P2pStatusCode.FAIL_REJECTED_BY_USER:
+ result = P2pStatus.REJECTED_BY_USER;
+ break;
+ }
+ return result;
+ }
+}
+
diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java
new file mode 100644
index 0000000..3e26828
--- /dev/null
+++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java
@@ -0,0 +1,2236 @@
+/*
+ * 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.p2p;
+
+import android.hardware.wifi.supplicant.V1_0.ISupplicant;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIface;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pNetwork;
+import android.hardware.wifi.supplicant.V1_0.IfaceType;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
+import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pGroupList;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.os.HwRemoteBinder;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.server.wifi.util.NativeUtil;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * Native calls sending requests to the P2P Hals, and callbacks for receiving P2P events
+ *
+ * {@hide}
+ */
+public class SupplicantP2pIfaceHal {
+ private static final boolean DBG = true;
+ private static final String TAG = "SupplicantP2pIfaceHal";
+ private static final int RESULT_NOT_VALID = -1;
+ private static final int DEFAULT_GROUP_OWNER_INTENT = 6;
+ private static final int DEFAULT_OPERATING_CLASS = 81;
+ /**
+ * Regex pattern for extracting the wps device type bytes.
+ * Matches a strings like the following: "<categ>-<OUI>-<subcateg>";
+ */
+ private static final Pattern WPS_DEVICE_TYPE_PATTERN =
+ Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$");
+
+ private Object mLock = new Object();
+
+ // Supplicant HAL HIDL interface objects
+ private IServiceManager mIServiceManager = null;
+ private ISupplicant mISupplicant = null;
+ private ISupplicantIface mHidlSupplicantIface = null;
+ private ISupplicantP2pIface mISupplicantP2pIface = null;
+ private final IServiceNotification mServiceNotificationCallback =
+ new IServiceNotification.Stub() {
+ public void onRegistration(String fqName, String name, boolean preexisting) {
+ synchronized (mLock) {
+ if (DBG) {
+ Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName
+ + ", " + name + " preexisting=" + preexisting);
+ }
+ if (!initSupplicantService() || !initSupplicantP2pIface()) {
+ Log.e(TAG, "initalizing ISupplicantIfaces failed.");
+ supplicantServiceDiedHandler();
+ } else {
+ Log.i(TAG, "Completed initialization of ISupplicant interfaces.");
+ }
+ }
+ }
+ };
+ private final HwRemoteBinder.DeathRecipient mServiceManagerDeathRecipient =
+ cookie -> {
+ Log.w(TAG, "IServiceManager died: cookie=" + cookie);
+ synchronized (mLock) {
+ supplicantServiceDiedHandler();
+ mIServiceManager = null; // Will need to register a new ServiceNotification
+ }
+ };
+ private final HwRemoteBinder.DeathRecipient mSupplicantDeathRecipient =
+ cookie -> {
+ Log.w(TAG, "ISupplicant/ISupplicantStaIface died: cookie=" + cookie);
+ synchronized (mLock) {
+ supplicantServiceDiedHandler();
+ }
+ };
+
+ private final WifiP2pMonitor mMonitor;
+ private SupplicantP2pIfaceCallback mCallback = null;
+
+ public SupplicantP2pIfaceHal(WifiP2pMonitor monitor) {
+ mMonitor = monitor;
+ }
+
+ private boolean linkToServiceManagerDeath() {
+ if (mIServiceManager == null) return false;
+ try {
+ if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on IServiceManager");
+ supplicantServiceDiedHandler();
+ mIServiceManager = null; // Will need to register a new ServiceNotification
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "IServiceManager.linkToDeath exception", e);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Registers a service notification for the ISupplicant service, which triggers intialization of
+ * the ISupplicantP2pIface
+ * @return true if the service notification was successfully registered
+ */
+ public boolean initialize() {
+ if (DBG) Log.i(TAG, "Registering ISupplicant service ready callback.");
+ synchronized (mLock) {
+ if (mIServiceManager != null) {
+ Log.i(TAG, "Supplicant HAL already initialized.");
+ // Already have an IServiceManager and serviceNotification registered, don't
+ // don't register another.
+ return true;
+ }
+ mISupplicant = null;
+ mISupplicantP2pIface = null;
+ try {
+ mIServiceManager = getServiceManagerMockable();
+ if (mIServiceManager == null) {
+ Log.e(TAG, "Failed to get HIDL Service Manager");
+ return false;
+ }
+ if (!linkToServiceManagerDeath()) {
+ return false;
+ }
+ /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it
+ exists */
+ if (!mIServiceManager.registerForNotifications(
+ ISupplicant.kInterfaceName, "", mServiceNotificationCallback)) {
+ Log.e(TAG, "Failed to register for notifications to "
+ + ISupplicant.kInterfaceName);
+ mIServiceManager = null; // Will need to register a new ServiceNotification
+ return false;
+ }
+
+ // Successful completion by the end of the 'try' block. This will prevent reporting
+ // proper initialization after exception is caught.
+ return true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: "
+ + e);
+ supplicantServiceDiedHandler();
+ }
+ return false;
+ }
+ }
+
+ private boolean linkToSupplicantDeath() {
+ if (mISupplicant == null) return false;
+ try {
+ if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on ISupplicant");
+ supplicantServiceDiedHandler();
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicant.linkToDeath exception", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean initSupplicantService() {
+ synchronized (mLock) {
+ try {
+ mISupplicant = getSupplicantMockable();
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicant.getService exception: " + e);
+ return false;
+ }
+ if (mISupplicant == null) {
+ Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup");
+ return false;
+ }
+ if (!linkToSupplicantDeath()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean linkToSupplicantP2pIfaceDeath() {
+ if (mISupplicantP2pIface == null) return false;
+ try {
+ if (!mISupplicantP2pIface.linkToDeath(mSupplicantDeathRecipient, 0)) {
+ Log.wtf(TAG, "Error on linkToDeath on ISupplicantP2pIface");
+ supplicantServiceDiedHandler();
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface.linkToDeath exception", e);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean initSupplicantP2pIface() {
+ synchronized (mLock) {
+ /** List all supplicant Ifaces */
+ final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList();
+ try {
+ mISupplicant.listInterfaces((SupplicantStatus status,
+ ArrayList<ISupplicant.IfaceInfo> ifaces) -> {
+ if (status.code != SupplicantStatusCode.SUCCESS) {
+ Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code);
+ return;
+ }
+ supplicantIfaces.addAll(ifaces);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicant.listInterfaces exception: " + e);
+ return false;
+ }
+ if (supplicantIfaces.size() == 0) {
+ Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup.");
+ return false;
+ }
+ SupplicantResult<ISupplicantIface> supplicantIface =
+ new SupplicantResult("getInterface()");
+ for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) {
+ if (ifaceInfo.type == IfaceType.P2P) {
+ try {
+ mISupplicant.getInterface(ifaceInfo,
+ (SupplicantStatus status, ISupplicantIface iface) -> {
+ if (status.code != SupplicantStatusCode.SUCCESS) {
+ Log.e(TAG, "Failed to get ISupplicantIface " + status.code);
+ return;
+ }
+ supplicantIface.setResult(status, iface);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicant.getInterface exception: " + e);
+ return false;
+ }
+ break;
+ }
+ }
+
+ if (supplicantIface.getResult() == null) {
+ Log.e(TAG, "initSupplicantP2pIface got null iface");
+ return false;
+ }
+ mISupplicantP2pIface = getP2pIfaceMockable(supplicantIface.getResult());
+ if (!linkToSupplicantP2pIfaceDeath()) {
+ return false;
+ }
+ }
+
+ if (mISupplicantP2pIface != null && mMonitor != null) {
+ // TODO(ender): Get rid of hard-coded interface name, which is
+ // assumed to be the group interface name in several other classes
+ // ("p2p0" should probably become getName()).
+ mCallback = new SupplicantP2pIfaceCallback("p2p0", mMonitor);
+ if (!registerCallback(mCallback)) {
+ Log.e(TAG, "Callback registration failed. Initialization incomplete.");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void supplicantServiceDiedHandler() {
+ synchronized (mLock) {
+ mISupplicant = null;
+ mISupplicantP2pIface = null;
+ }
+ }
+
+
+ /**
+ * Signals whether Initialization completed successfully.
+ */
+ public boolean isInitializationStarted() {
+ return mIServiceManager != null;
+ }
+
+ /**
+ * Signals whether Initialization completed successfully. Only necessary for testing, is not
+ * needed to guard calls etc.
+ */
+ public boolean isInitializationComplete() {
+ return mISupplicantP2pIface != null;
+ }
+
+ /**
+ * Wrapper functions to access static HAL methods, created to be mockable in unit tests
+ */
+ protected IServiceManager getServiceManagerMockable() throws RemoteException {
+ return IServiceManager.getService();
+ }
+
+ protected ISupplicant getSupplicantMockable() throws RemoteException {
+ return ISupplicant.getService();
+ }
+
+ protected ISupplicantP2pIface getP2pIfaceMockable(ISupplicantIface iface) {
+ return ISupplicantP2pIface.asInterface(iface.asBinder());
+ }
+
+ protected ISupplicantP2pNetwork getP2pNetworkMockable(ISupplicantNetwork network) {
+ return ISupplicantP2pNetwork.asInterface(network.asBinder());
+ }
+
+ protected static void logd(String s) {
+ if (DBG) Log.d(TAG, s);
+ }
+
+ protected static void logCompletion(String operation, SupplicantStatus status) {
+ if (status == null) {
+ Log.w(TAG, operation + " failed: no status code returned.");
+ } else if (status.code == SupplicantStatusCode.SUCCESS) {
+ logd(operation + " completed successfully.");
+ } else {
+ Log.w(TAG, operation + " failed: " + status.code + " (" + status.debugMessage + ")");
+ }
+ }
+
+
+ /**
+ * Returns false if SupplicantP2pIface is null, and logs failure to call methodStr
+ */
+ private boolean checkSupplicantP2pIfaceAndLogFailure(String method) {
+ if (mISupplicantP2pIface == null) {
+ Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null");
+ return false;
+ }
+ return true;
+ }
+
+ private int wpsInfoToConfigMethod(int info) {
+ switch (info) {
+ case WpsInfo.PBC:
+ return ISupplicantP2pIface.WpsProvisionMethod.PBC;
+
+ case WpsInfo.DISPLAY:
+ return ISupplicantP2pIface.WpsProvisionMethod.DISPLAY;
+
+ case WpsInfo.KEYPAD:
+ case WpsInfo.LABEL:
+ return ISupplicantP2pIface.WpsProvisionMethod.KEYPAD;
+
+ default:
+ Log.e(TAG, "Unsupported WPS provision method: " + info);
+ return RESULT_NOT_VALID;
+ }
+ }
+
+ /**
+ * Retrieves the name of the network interface.
+ *
+ * @return name Name of the network interface, e.g., wlan0
+ */
+ public String getName() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("getName")) return null;
+ SupplicantResult<String> result = new SupplicantResult("getName()");
+
+ try {
+ mISupplicantP2pIface.getName(
+ (SupplicantStatus status, String name) -> {
+ result.setResult(status, name);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.getResult();
+ }
+ }
+
+
+ /**
+ * Register for callbacks from this interface.
+ *
+ * These callbacks are invoked for events that are specific to this interface.
+ * Registration of multiple callback objects is supported. These objects must
+ * be automatically deleted when the corresponding client process is dead or
+ * if this interface is removed.
+ *
+ * @param receiver An instance of the |ISupplicantP2pIfaceCallback| HIDL
+ * interface object.
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean registerCallback(ISupplicantP2pIfaceCallback receiver) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("registerCallback")) return false;
+ SupplicantResult<Void> result = new SupplicantResult("registerCallback()");
+ try {
+ result.setResult(mISupplicantP2pIface.registerCallback(receiver));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Initiate a P2P service discovery with a (optional) timeout.
+ *
+ * @param timeout Max time to be spent is peforming discovery.
+ * Set to 0 to indefinely continue discovery untill and explicit
+ * |stopFind| is sent.
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean find(int timeout) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("find")) return false;
+
+ if (timeout < 0) {
+ Log.e(TAG, "Invalid timeout value: " + timeout);
+ return false;
+ }
+ SupplicantResult<Void> result = new SupplicantResult("find(" + timeout + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.find(timeout));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Stop an ongoing P2P service discovery.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean stopFind() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("stopFind")) return false;
+ SupplicantResult<Void> result = new SupplicantResult("stopFind()");
+ try {
+ result.setResult(mISupplicantP2pIface.stopFind());
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Flush P2P peer table and state.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean flush() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("flush")) return false;
+ SupplicantResult<Void> result = new SupplicantResult("flush()");
+ try {
+ result.setResult(mISupplicantP2pIface.flush());
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * This command can be used to flush all services from the
+ * device.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean serviceFlush() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("serviceFlush")) return false;
+ SupplicantResult<Void> result = new SupplicantResult("serviceFlush()");
+ try {
+ result.setResult(mISupplicantP2pIface.flushServices());
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Turn on/off power save mode for the interface.
+ *
+ * @param groupIfName Group interface name to use.
+ * @param enable Indicate if power save is to be turned on/off.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean setPowerSave(String groupIfName, boolean enable) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setPowerSave")) return false;
+ SupplicantResult<Void> result = new SupplicantResult(
+ "setPowerSave(" + groupIfName + ", " + enable + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.setPowerSave(groupIfName, enable));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Set the Maximum idle time in seconds for P2P groups.
+ * This value controls how long a P2P group is maintained after there
+ * is no other members in the group. As a group owner, this means no
+ * associated stations in the group. As a P2P client, this means no
+ * group owner seen in scan results.
+ *
+ * @param groupIfName Group interface name to use.
+ * @param timeoutInSec Timeout value in seconds.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean setGroupIdle(String groupIfName, int timeoutInSec) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setGroupIdle")) return false;
+ // Basic checking here. Leave actual parameter validation to supplicant.
+ if (timeoutInSec < 0) {
+ Log.e(TAG, "Invalid group timeout value " + timeoutInSec);
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "setGroupIdle(" + groupIfName + ", " + timeoutInSec + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.setGroupIdle(groupIfName, timeoutInSec));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Set the postfix to be used for P2P SSID's.
+ *
+ * @param postfix String to be appended to SSID.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean setSsidPostfix(String postfix) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return false;
+ // Basic checking here. Leave actual parameter validation to supplicant.
+ if (postfix == null) {
+ Log.e(TAG, "Invalid SSID postfix value (null).");
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult("setSsidPostfix(" + postfix + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.setSsidPostfix(
+ NativeUtil.decodeSsid("\"" + postfix + "\"")));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Could not decode SSID.", e);
+ return false;
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Start P2P group formation with a discovered P2P peer. This includes
+ * optional group owner negotiation, group interface setup, provisioning,
+ * and establishing data connection.
+ *
+ * @param config Configuration to use to connect to remote device.
+ * @param joinExistingGroup Indicates that this is a command to join an
+ * existing group as a client. It skips the group owner negotiation
+ * part. This must send a Provision Discovery Request message to the
+ * target group owner before associating for WPS provisioning.
+ *
+ * @return String containing generated pin, if selected provision method
+ * uses PIN.
+ */
+ public String connect(WifiP2pConfig config, boolean joinExistingGroup) {
+ if (config == null) return null;
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return null;
+
+ if (config == null) {
+ Log.e(TAG, "Could not connect: null config.");
+ return null;
+ }
+
+ if (config.deviceAddress == null) {
+ Log.e(TAG, "Could not parse null mac address.");
+ return null;
+ }
+
+ if (config.wps.setup == WpsInfo.PBC && !TextUtils.isEmpty(config.wps.pin)) {
+ Log.e(TAG, "Expected empty pin for PBC.");
+ return null;
+ }
+
+ byte[] peerAddress = null;
+ try {
+ peerAddress = NativeUtil.macAddressToByteArray(config.deviceAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse peer mac address.", e);
+ return null;
+ }
+
+ int provisionMethod = wpsInfoToConfigMethod(config.wps.setup);
+ if (provisionMethod == RESULT_NOT_VALID) {
+ Log.e(TAG, "Invalid WPS config method: " + config.wps.setup);
+ return null;
+ }
+ // NOTE: preSelectedPin cannot be null, otherwise hal would crash.
+ String preSelectedPin = TextUtils.isEmpty(config.wps.pin) ? "" : config.wps.pin;
+ boolean persistent = (config.netId == WifiP2pGroup.PERSISTENT_NET_ID);
+
+ int goIntent = 0;
+ if (!joinExistingGroup) {
+ int groupOwnerIntent = config.groupOwnerIntent;
+ if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
+ groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
+ }
+ goIntent = groupOwnerIntent;
+ }
+
+ SupplicantResult<String> result = new SupplicantResult(
+ "connect(" + config.deviceAddress + ")");
+ try {
+ mISupplicantP2pIface.connect(
+ peerAddress, provisionMethod, preSelectedPin, joinExistingGroup,
+ persistent, goIntent,
+ (SupplicantStatus status, String generatedPin) -> {
+ result.setResult(status, generatedPin);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.getResult();
+ }
+ }
+
+ /**
+ * Cancel an ongoing P2P group formation and joining-a-group related
+ * operation. This operation unauthorizes the specific peer device (if any
+ * had been authorized to start group formation), stops P2P find (if in
+ * progress), stops pending operations for join-a-group, and removes the
+ * P2P group interface (if one was used) that is in the WPS provisioning
+ * step. If the WPS provisioning step has been completed, the group is not
+ * terminated.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean cancelConnect() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("cancelConnect")) return false;
+ SupplicantResult<Void> result = new SupplicantResult("cancelConnect()");
+ try {
+ result.setResult(mISupplicantP2pIface.cancelConnect());
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Send P2P provision discovery request to the specified peer. The
+ * parameters for this command are the P2P device address of the peer and the
+ * desired configuration method.
+ *
+ * @param config Config class describing peer setup.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean provisionDiscovery(WifiP2pConfig config) {
+ if (config == null) return false;
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("provisionDiscovery")) return false;
+
+ int targetMethod = wpsInfoToConfigMethod(config.wps.setup);
+ if (targetMethod == RESULT_NOT_VALID) {
+ Log.e(TAG, "Unrecognized WPS configuration method: " + config.wps.setup);
+ return false;
+ }
+ if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.DISPLAY) {
+ // We are doing display, so provision discovery is keypad.
+ targetMethod = ISupplicantP2pIface.WpsProvisionMethod.KEYPAD;
+ } else if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.KEYPAD) {
+ // We are doing keypad, so provision discovery is display.
+ targetMethod = ISupplicantP2pIface.WpsProvisionMethod.DISPLAY;
+ }
+
+ if (config.deviceAddress == null) {
+ Log.e(TAG, "Cannot parse null mac address.");
+ return false;
+ }
+ byte[] macAddress = null;
+ try {
+ macAddress = NativeUtil.macAddressToByteArray(config.deviceAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse peer mac address.", e);
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "provisionDiscovery(" + config.deviceAddress + ", " + config.wps.setup + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.provisionDiscovery(macAddress, targetMethod));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Invite a device to a persistent group.
+ * If the peer device is the group owner of the persistent group, the peer
+ * parameter is not needed. Otherwise it is used to specify which
+ * device to invite. |goDeviceAddress| parameter may be used to override
+ * the group owner device address for Invitation Request should it not be
+ * known for some reason (this should not be needed in most cases).
+ *
+ * @param group Group object to use.
+ * @param peerAddress MAC address of the device to invite.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean invite(WifiP2pGroup group, String peerAddress) {
+ if (TextUtils.isEmpty(peerAddress)) return false;
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("invite")) return false;
+ if (group == null) {
+ Log.e(TAG, "Cannot invite to null group.");
+ return false;
+ }
+
+ if (group.getOwner() == null) {
+ Log.e(TAG, "Cannot invite to group with null owner.");
+ return false;
+ }
+
+ if (group.getOwner().deviceAddress == null) {
+ Log.e(TAG, "Group owner has no mac address.");
+ return false;
+ }
+
+ byte[] ownerMacAddress = null;
+ try {
+ ownerMacAddress = NativeUtil.macAddressToByteArray(group.getOwner().deviceAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Group owner mac address parse error.", e);
+ return false;
+ }
+
+ if (peerAddress == null) {
+ Log.e(TAG, "Cannot parse peer mac address.");
+ return false;
+ }
+
+ byte[] peerMacAddress;
+ try {
+ peerMacAddress = NativeUtil.macAddressToByteArray(peerAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Peer mac address parse error.", e);
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "invite(" + group.getInterface() + ", " + group.getOwner().deviceAddress
+ + ", " + peerAddress + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.invite(
+ group.getInterface(), ownerMacAddress, peerMacAddress));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Reject connection attempt from a peer (specified with a device
+ * address). This is a mechanism to reject a pending group owner negotiation
+ * with a peer and request to automatically block any further connection or
+ * discovery of the peer.
+ *
+ * @param peerAddress MAC address of the device to reject.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean reject(String peerAddress) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("reject")) return false;
+
+ if (peerAddress == null) {
+ Log.e(TAG, "Cannot parse rejected peer's mac address.");
+ return false;
+ }
+ byte[] macAddress = null;
+ try {
+ macAddress = NativeUtil.macAddressToByteArray(peerAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse peer mac address.", e);
+ return false;
+ }
+
+ SupplicantResult<Void> result =
+ new SupplicantResult("reject(" + peerAddress + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.reject(macAddress));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Gets the MAC address of the device.
+ *
+ * @return MAC address of the device.
+ */
+ public String getDeviceAddress() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("getDeviceAddress")) return null;
+ SupplicantResult<String> result = new SupplicantResult("getDeviceAddress()");
+ try {
+ mISupplicantP2pIface.getDeviceAddress((SupplicantStatus status, byte[] address) -> {
+ String parsedAddress = null;
+ try {
+ parsedAddress = NativeUtil.macAddressFromByteArray(address);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not process reported address.", e);
+ }
+ result.setResult(status, parsedAddress);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ return null;
+ }
+
+ return result.getResult();
+ }
+ }
+
+
+ /**
+ * Gets the operational SSID of the device.
+ *
+ * @param address MAC address of the peer.
+ *
+ * @return SSID of the device.
+ */
+ public String getSsid(String address) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("getSsid")) return null;
+
+ if (address == null) {
+ Log.e(TAG, "Cannot parse peer mac address.");
+ return null;
+ }
+ byte[] macAddress = null;
+ try {
+ macAddress = NativeUtil.macAddressToByteArray(address);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse mac address.", e);
+ return null;
+ }
+
+ SupplicantResult<String> result =
+ new SupplicantResult("getSsid(" + address + ")");
+ try {
+ mISupplicantP2pIface.getSsid(
+ macAddress, (SupplicantStatus status, ArrayList<Byte> ssid) -> {
+ String ssidString = null;
+ if (ssid != null) {
+ try {
+ ssidString = NativeUtil.encodeSsid(ssid);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not encode SSID.", e);
+ }
+ }
+ result.setResult(status, ssidString);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ return null;
+ }
+
+ return result.getResult();
+ }
+ }
+
+
+ /**
+ * Reinvoke a device from a persistent group.
+ *
+ * @param networkId Used to specify the persistent group.
+ * @param peerAddress MAC address of the device to reinvoke.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean reinvoke(int networkId, String peerAddress) {
+ if (TextUtils.isEmpty(peerAddress) || networkId < 0) return false;
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("reinvoke")) return false;
+ if (peerAddress == null) {
+ Log.e(TAG, "Cannot parse peer mac address.");
+ return false;
+ }
+ byte[] macAddress = null;
+ try {
+ macAddress = NativeUtil.macAddressToByteArray(peerAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse mac address.", e);
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "reinvoke(" + networkId + ", " + peerAddress + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.reinvoke(networkId, macAddress));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Set up a P2P group owner manually (i.e., without group owner
+ * negotiation with a specific peer). This is also known as autonomous
+ * group owner.
+ *
+ * @param networkId Used to specify the restart of a persistent group.
+ * @param isPersistent Used to request a persistent group to be formed.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean groupAdd(int networkId, boolean isPersistent) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("groupAdd")) return false;
+ SupplicantResult<Void> result =
+ new SupplicantResult("groupAdd(" + networkId + ", " + isPersistent + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.addGroup(isPersistent, networkId));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+ /**
+ * Set up a P2P group owner manually.
+ * This is a helper method that invokes groupAdd(networkId, isPersistent) internally.
+ *
+ * @param isPersistent Used to request a persistent group to be formed.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean groupAdd(boolean isPersistent) {
+ // Supplicant expects networkId to be -1 if not supplied.
+ return groupAdd(-1, isPersistent);
+ }
+
+
+ /**
+ * Terminate a P2P group. If a new virtual network interface was used for
+ * the group, it must also be removed. The network interface name of the
+ * group interface is used as a parameter for this command.
+ *
+ * @param groupName Group interface name to use.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean groupRemove(String groupName) {
+ if (TextUtils.isEmpty(groupName)) return false;
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("groupRemove")) return false;
+ SupplicantResult<Void> result = new SupplicantResult("groupRemove(" + groupName + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.removeGroup(groupName));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Gets the capability of the group which the device is a
+ * member of.
+ *
+ * @param peerAddress MAC address of the peer.
+ *
+ * @return combination of |GroupCapabilityMask| values.
+ */
+ public int getGroupCapability(String peerAddress) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("getGroupCapability")) {
+ return RESULT_NOT_VALID;
+ }
+
+ if (peerAddress == null) {
+ Log.e(TAG, "Cannot parse peer mac address.");
+ return RESULT_NOT_VALID;
+ }
+ byte[] macAddress = null;
+ try {
+ macAddress = NativeUtil.macAddressToByteArray(peerAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse group address.", e);
+ return RESULT_NOT_VALID;
+ }
+
+ SupplicantResult<Integer> capability = new SupplicantResult(
+ "getGroupCapability(" + peerAddress + ")");
+ try {
+ mISupplicantP2pIface.getGroupCapability(
+ macAddress, (SupplicantStatus status, int cap) -> {
+ capability.setResult(status, cap);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ if (!capability.isSuccess()) {
+ return RESULT_NOT_VALID;
+ }
+
+ return capability.getResult();
+ }
+ }
+
+
+ /**
+ * Configure Extended Listen Timing.
+ *
+ * If enabled, listen state must be entered every |intervalInMillis| for at
+ * least |periodInMillis|. Both values have acceptable range of 1-65535
+ * (with interval obviously having to be larger than or equal to duration).
+ * If the P2P module is not idle at the time the Extended Listen Timing
+ * timeout occurs, the Listen State operation must be skipped.
+ *
+ * @param enable Enables or disables listening.
+ * @param periodInMillis Period in milliseconds.
+ * @param intervalInMillis Interval in milliseconds.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean configureExtListen(boolean enable, int periodInMillis, int intervalInMillis) {
+ if (enable && intervalInMillis < periodInMillis) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("configureExtListen")) return false;
+
+ // If listening is disabled, wpa supplicant expects zeroes.
+ if (!enable) {
+ periodInMillis = 0;
+ intervalInMillis = 0;
+ }
+
+ // Verify that the integers are not negative. Leave actual parameter validation to
+ // supplicant.
+ if (periodInMillis < 0 || intervalInMillis < 0) {
+ Log.e(TAG, "Invalid parameters supplied to configureExtListen: " + periodInMillis
+ + ", " + intervalInMillis);
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "configureExtListen(" + periodInMillis + ", " + intervalInMillis + ")");
+ try {
+ result.setResult(
+ mISupplicantP2pIface.configureExtListen(periodInMillis, intervalInMillis));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Set P2P Listen channel and operating chanel.
+ *
+ * @param listenChannel Wifi channel. eg, 1, 6, 11.
+ * @param operatingChannel Wifi channel. eg, 1, 6, 11.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean setListenChannel(int listenChannel, int operatingChannel) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setListenChannel")) return false;
+
+ if (listenChannel >= 1 && listenChannel <= 11) {
+ SupplicantResult<Void> result = new SupplicantResult(
+ "setListenChannel(" + listenChannel + ", " + DEFAULT_OPERATING_CLASS + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.setListenChannel(
+ listenChannel, DEFAULT_OPERATING_CLASS));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ if (!result.isSuccess()) {
+ return false;
+ }
+ } else if (listenChannel != 0) {
+ // listenChannel == 0 does not set any listen channel.
+ return false;
+ }
+
+ if (operatingChannel >= 0 && operatingChannel <= 165) {
+ ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>();
+ // operatingChannel == 0 enables all freqs.
+ if (operatingChannel >= 1 && operatingChannel <= 165) {
+ int freq = (operatingChannel <= 14 ? 2407 : 5000) + operatingChannel * 5;
+ ISupplicantP2pIface.FreqRange range1 = new ISupplicantP2pIface.FreqRange();
+ range1.min = 1000;
+ range1.max = freq - 5;
+ ISupplicantP2pIface.FreqRange range2 = new ISupplicantP2pIface.FreqRange();
+ range2.min = freq + 5;
+ range2.max = 6000;
+ ranges.add(range1);
+ ranges.add(range2);
+ }
+ SupplicantResult<Void> result = new SupplicantResult(
+ "setDisallowedFrequencies(" + ranges + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.setDisallowedFrequencies(ranges));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ return false;
+ }
+ }
+
+
+ /**
+ * This command can be used to add a upnp/bonjour service.
+ *
+ * @param servInfo List of service queries.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean serviceAdd(WifiP2pServiceInfo servInfo) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("serviceAdd")) return false;
+
+ if (servInfo == null) {
+ Log.e(TAG, "Null service info passed.");
+ return false;
+ }
+
+ for (String s : servInfo.getSupplicantQueryList()) {
+ if (s == null) {
+ Log.e(TAG, "Invalid service description (null).");
+ return false;
+ }
+
+ String[] data = s.split(" ");
+ if (data.length < 3) {
+ Log.e(TAG, "Service specification invalid: " + s);
+ return false;
+ }
+
+ SupplicantResult<Void> result = null;
+ try {
+ if ("upnp".equals(data[0])) {
+ int version = 0;
+ try {
+ version = Integer.parseInt(data[1], 16);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "UPnP Service specification invalid: " + s, e);
+ return false;
+ }
+
+ result = new SupplicantResult(
+ "addUpnpService(" + data[1] + ", " + data[2] + ")");
+ result.setResult(mISupplicantP2pIface.addUpnpService(version, data[2]));
+ } else if ("bonjour".equals(data[0])) {
+ if (data[1] != null && data[2] != null) {
+ ArrayList<Byte> request = null;
+ ArrayList<Byte> response = null;
+ try {
+ request = NativeUtil.byteArrayToArrayList(
+ NativeUtil.hexStringToByteArray(data[1]));
+ response = NativeUtil.byteArrayToArrayList(
+ NativeUtil.hexStringToByteArray(data[2]));
+ } catch (Exception e) {
+ Log.e(TAG, "Invalid bonjour service description.");
+ return false;
+ }
+ result = new SupplicantResult(
+ "addBonjourService(" + data[1] + ", " + data[2] + ")");
+ result.setResult(
+ mISupplicantP2pIface.addBonjourService(request, response));
+ }
+ } else {
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ if (result == null || !result.isSuccess()) return false;
+ }
+
+ return true;
+ }
+ }
+
+
+ /**
+ * This command can be used to remove a upnp/bonjour service.
+ *
+ * @param servInfo List of service queries.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean serviceRemove(WifiP2pServiceInfo servInfo) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("serviceRemove")) return false;
+
+ if (servInfo == null) {
+ Log.e(TAG, "Null service info passed.");
+ return false;
+ }
+
+ for (String s : servInfo.getSupplicantQueryList()) {
+ if (s == null) {
+ Log.e(TAG, "Invalid service description (null).");
+ return false;
+ }
+
+ String[] data = s.split(" ");
+ if (data.length < 3) {
+ Log.e(TAG, "Service specification invalid: " + s);
+ return false;
+ }
+
+ SupplicantResult<Void> result = null;
+ try {
+ if ("upnp".equals(data[0])) {
+ int version = 0;
+ try {
+ version = Integer.parseInt(data[1], 16);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "UPnP Service specification invalid: " + s, e);
+ return false;
+ }
+ result = new SupplicantResult(
+ "removeUpnpService(" + data[1] + ", " + data[2] + ")");
+ result.setResult(mISupplicantP2pIface.removeUpnpService(version, data[2]));
+ } else if ("bonjour".equals(data[0])) {
+ if (data[1] != null) {
+ ArrayList<Byte> request = null;
+ try {
+ request = NativeUtil.byteArrayToArrayList(
+ NativeUtil.hexStringToByteArray(data[1]));
+ } catch (Exception e) {
+ Log.e(TAG, "Invalid bonjour service description.");
+ return false;
+ }
+ result = new SupplicantResult("removeBonjourService(" + data[1] + ")");
+ result.setResult(mISupplicantP2pIface.removeBonjourService(request));
+ }
+ } else {
+ Log.e(TAG, "Unknown / unsupported P2P service requested: " + data[0]);
+ return false;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ if (result == null || !result.isSuccess()) return false;
+ }
+
+ return true;
+ }
+ }
+
+
+ /**
+ * Schedule a P2P service discovery request. The parameters for this command
+ * are the device address of the peer device (or 00:00:00:00:00:00 for
+ * wildcard query that is sent to every discovered P2P peer that supports
+ * service discovery) and P2P Service Query TLV(s) as hexdump.
+ *
+ * @param peerAddress MAC address of the device to discover.
+ * @param query Hex dump of the query data.
+ * @return identifier Identifier for the request. Can be used to cancel the
+ * request.
+ */
+ public String requestServiceDiscovery(String peerAddress, String query) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("requestServiceDiscovery")) return null;
+
+ if (peerAddress == null) {
+ Log.e(TAG, "Cannot parse peer mac address.");
+ return null;
+ }
+ byte[] macAddress = null;
+ try {
+ macAddress = NativeUtil.macAddressToByteArray(peerAddress);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not process peer MAC address.", e);
+ return null;
+ }
+
+ if (query == null) {
+ Log.e(TAG, "Cannot parse service discovery query: " + query);
+ return null;
+ }
+ ArrayList<Byte> binQuery = null;
+ try {
+ binQuery = NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(query));
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse service query.", e);
+ return null;
+ }
+
+ SupplicantResult<Long> result = new SupplicantResult(
+ "requestServiceDiscovery(" + peerAddress + ", " + query + ")");
+ try {
+ mISupplicantP2pIface.requestServiceDiscovery(
+ macAddress, binQuery,
+ (SupplicantStatus status, long identifier) -> {
+ result.setResult(status, new Long(identifier));
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ Long value = result.getResult();
+ if (value == null) return null;
+ return value.toString();
+ }
+ }
+
+
+ /**
+ * Cancel a previous service discovery request.
+ *
+ * @param identifier Identifier for the request to cancel.
+ * @return true, if operation was successful.
+ */
+ public boolean cancelServiceDiscovery(String identifier) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("cancelServiceDiscovery")) return false;
+ if (identifier == null) {
+ Log.e(TAG, "cancelServiceDiscovery requires a valid tag.");
+ return false;
+ }
+
+ long id = 0;
+ try {
+ id = Long.parseLong(identifier);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Service discovery identifier invalid: " + identifier, e);
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "cancelServiceDiscovery(" + identifier + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.cancelServiceDiscovery(id));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Send driver command to set Miracast mode.
+ *
+ * @param mode Mode of Miracast.
+ * @return true, if operation was successful.
+ */
+ public boolean setMiracastMode(int mode) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setMiracastMode")) return false;
+ byte targetMode = ISupplicantP2pIface.MiracastMode.DISABLED;
+
+ switch (mode) {
+ case WifiP2pManager.MIRACAST_SOURCE:
+ targetMode = ISupplicantP2pIface.MiracastMode.SOURCE;
+ break;
+
+ case WifiP2pManager.MIRACAST_SINK:
+ targetMode = ISupplicantP2pIface.MiracastMode.SINK;
+ break;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "setMiracastMode(" + mode + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.setMiracastMode(targetMode));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Initiate WPS Push Button setup.
+ * The PBC operation requires that a button is also pressed at the
+ * AP/Registrar at about the same time (2 minute window).
+ *
+ * @param groupIfName Group interface name to use.
+ * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard.
+ * @return true, if operation was successful.
+ */
+ public boolean startWpsPbc(String groupIfName, String bssid) {
+ if (TextUtils.isEmpty(groupIfName)) {
+ Log.e(TAG, "Group name required when requesting WPS PBC. Got (" + groupIfName + ")");
+ return false;
+ }
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPbc")) return false;
+ // Null values should be fine, since bssid can be empty.
+ byte[] macAddress = null;
+ try {
+ macAddress = NativeUtil.macAddressToByteArray(bssid);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse BSSID.", e);
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "startWpsPbc(" + groupIfName + ", " + bssid + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.startWpsPbc(groupIfName, macAddress));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Initiate WPS Pin Keypad setup.
+ *
+ * @param groupIfName Group interface name to use.
+ * @param pin 8 digit pin to be used.
+ * @return true, if operation was successful.
+ */
+ public boolean startWpsPinKeypad(String groupIfName, String pin) {
+ if (TextUtils.isEmpty(groupIfName) || TextUtils.isEmpty(pin)) return false;
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinKeypad")) return false;
+ if (groupIfName == null) {
+ Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
+ return false;
+ }
+ if (pin == null) {
+ Log.e(TAG, "PIN required when requesting WPS KEYPAD.");
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "startWpsPinKeypad(" + groupIfName + ", " + pin + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.startWpsPinKeypad(groupIfName, pin));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Initiate WPS Pin Display setup.
+ *
+ * @param groupIfName Group interface name to use.
+ * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard.
+ * @return generated pin if operation was successful, null otherwise.
+ */
+ public String startWpsPinDisplay(String groupIfName, String bssid) {
+ if (TextUtils.isEmpty(groupIfName)) return null;
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinDisplay")) return null;
+ if (groupIfName == null) {
+ Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
+ return null;
+ }
+
+ // Null values should be fine, since bssid can be empty.
+ byte[] macAddress = null;
+ try {
+ macAddress = NativeUtil.macAddressToByteArray(bssid);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse BSSID.", e);
+ return null;
+ }
+
+ SupplicantResult<String> result = new SupplicantResult(
+ "startWpsPinDisplay(" + groupIfName + ", " + bssid + ")");
+ try {
+ mISupplicantP2pIface.startWpsPinDisplay(
+ groupIfName, macAddress,
+ (SupplicantStatus status, String generatedPin) -> {
+ result.setResult(status, generatedPin);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.getResult();
+ }
+ }
+
+
+ /**
+ * Cancel any ongoing WPS operations.
+ *
+ * @param groupIfName Group interface name to use.
+ * @return true, if operation was successful.
+ */
+ public boolean cancelWps(String groupIfName) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("cancelWps")) return false;
+ if (groupIfName == null) {
+ Log.e(TAG, "Group name required when requesting WPS KEYPAD.");
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "cancelWps(" + groupIfName + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.cancelWps(groupIfName));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Enable/Disable Wifi Display.
+ *
+ * @param enable true to enable, false to disable.
+ * @return true, if operation was successful.
+ */
+ public boolean enableWfd(boolean enable) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("enableWfd")) return false;
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "enableWfd(" + enable + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.enableWfd(enable));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+
+ /**
+ * Set Wifi Display device info.
+ *
+ * @param info WFD device info as described in section 5.1.2 of WFD technical
+ * specification v1.0.0.
+ * @return true, if operation was successful.
+ */
+ public boolean setWfdDeviceInfo(String info) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setWfdDeviceInfo")) return false;
+
+ if (info == null) {
+ Log.e(TAG, "Cannot parse null WFD info string.");
+ return false;
+ }
+ byte[] wfdInfo = null;
+ try {
+ wfdInfo = NativeUtil.hexStringToByteArray(info);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not parse WFD Device Info string.");
+ return false;
+ }
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "setWfdDeviceInfo(" + info + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.setWfdDeviceInfo(wfdInfo));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+ /**
+ * Remove network with provided id.
+ *
+ * @param networkId Id of the network to lookup.
+ * @return true, if operation was successful.
+ */
+ public boolean removeNetwork(int networkId) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("removeNetwork")) return false;
+
+ SupplicantResult<Void> result = new SupplicantResult(
+ "removeNetwork(" + networkId + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.removeNetwork(networkId));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+
+ return result.isSuccess();
+ }
+ }
+
+ /**
+ * List the networks saved in wpa_supplicant.
+ *
+ * @return List of network ids.
+ */
+ private List<Integer> listNetworks() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("listNetworks")) return null;
+ SupplicantResult<ArrayList> result = new SupplicantResult("listNetworks()");
+ try {
+ mISupplicantP2pIface.listNetworks(
+ (SupplicantStatus status, ArrayList<Integer> networkIds) -> {
+ result.setResult(status, networkIds);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.getResult();
+ }
+ }
+
+ /**
+ * Get the supplicant P2p network object for the specified network ID.
+ *
+ * @param networkId Id of the network to lookup.
+ * @return ISupplicantP2pNetwork instance on success, null on failure.
+ */
+ private ISupplicantP2pNetwork getNetwork(int networkId) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("getNetwork")) return null;
+ SupplicantResult<ISupplicantNetwork> result =
+ new SupplicantResult("getNetwork(" + networkId + ")");
+ try {
+ mISupplicantP2pIface.getNetwork(
+ networkId,
+ (SupplicantStatus status, ISupplicantNetwork network) -> {
+ result.setResult(status, network);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ if (result.getResult() == null) {
+ Log.e(TAG, "getNetwork got null network");
+ return null;
+ }
+ return getP2pNetworkMockable(result.getResult());
+ }
+ }
+
+ /**
+ * Get the persistent group list from wpa_supplicant's p2p mgmt interface
+ *
+ * @param groups WifiP2pGroupList to store persistent groups in
+ * @return true, if list has been modified.
+ */
+ public boolean loadGroups(WifiP2pGroupList groups) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("loadGroups")) return false;
+ List<Integer> networkIds = listNetworks();
+ if (networkIds == null || networkIds.isEmpty()) {
+ return false;
+ }
+ for (Integer networkId : networkIds) {
+ ISupplicantP2pNetwork network = getNetwork(networkId);
+ if (network == null) {
+ Log.e(TAG, "Failed to retrieve network object for " + networkId);
+ continue;
+ }
+ SupplicantResult<Boolean> resultIsCurrent =
+ new SupplicantResult("isCurrent(" + networkId + ")");
+ try {
+ network.isCurrent(
+ (SupplicantStatus status, boolean isCurrent) -> {
+ resultIsCurrent.setResult(status, isCurrent);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ /** Skip the current network, if we're somehow getting networks from the p2p GO
+ interface, instead of p2p mgmt interface*/
+ if (!resultIsCurrent.isSuccess() || resultIsCurrent.getResult()) {
+ Log.i(TAG, "Skipping current network");
+ continue;
+ }
+
+ WifiP2pGroup group = new WifiP2pGroup();
+ group.setNetworkId(networkId);
+
+ // Now get the ssid, bssid and other flags for this network.
+ SupplicantResult<ArrayList> resultSsid =
+ new SupplicantResult("getSsid(" + networkId + ")");
+ try {
+ network.getSsid(
+ (SupplicantStatus status, ArrayList<Byte> ssid) -> {
+ resultSsid.setResult(status, ssid);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ if (resultSsid.isSuccess() && resultSsid.getResult() != null
+ && !resultSsid.getResult().isEmpty()) {
+ group.setNetworkName(NativeUtil.removeEnclosingQuotes(
+ NativeUtil.encodeSsid(resultSsid.getResult())));
+ }
+
+ SupplicantResult<byte[]> resultBssid =
+ new SupplicantResult("getBssid(" + networkId + ")");
+ try {
+ network.getBssid(
+ (SupplicantStatus status, byte[] bssid) -> {
+ resultBssid.setResult(status, bssid);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ if (resultBssid.isSuccess() && !ArrayUtils.isEmpty(resultBssid.getResult())) {
+ WifiP2pDevice device = new WifiP2pDevice();
+ device.deviceAddress =
+ NativeUtil.macAddressFromByteArray(resultBssid.getResult());
+ group.setOwner(device);
+ }
+
+ SupplicantResult<Boolean> resultIsGo =
+ new SupplicantResult("isGo(" + networkId + ")");
+ try {
+ network.isGo(
+ (SupplicantStatus status, boolean isGo) -> {
+ resultIsGo.setResult(status, isGo);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ if (resultIsGo.isSuccess()) {
+ group.setIsGroupOwner(resultIsGo.getResult());
+ }
+ groups.add(group);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Set WPS device name.
+ *
+ * @param name String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsDeviceName(String name) {
+ if (name == null) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceName")) return false;
+ SupplicantResult<Void> result = new SupplicantResult(
+ "setWpsDeviceName(" + name + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.setWpsDeviceName(name));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+ /**
+ * Set WPS device type.
+ *
+ * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsDeviceType(String typeStr) {
+ try {
+ Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr);
+ if (!match.find() || match.groupCount() != 3) {
+ Log.e(TAG, "Malformed WPS device type " + typeStr);
+ return false;
+ }
+ short categ = Short.parseShort(match.group(1));
+ byte[] oui = NativeUtil.hexStringToByteArray(match.group(2));
+ short subCateg = Short.parseShort(match.group(3));
+
+ byte[] bytes = new byte[8];
+ ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
+ byteBuffer.putShort(categ);
+ byteBuffer.put(oui);
+ byteBuffer.putShort(subCateg);
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceType")) return false;
+ SupplicantResult<Void> result = new SupplicantResult(
+ "setWpsDeviceType(" + typeStr + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.setWpsDeviceType(bytes));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + typeStr, e);
+ return false;
+ }
+ }
+
+ /**
+ * Set WPS config methods
+ *
+ * @param configMethodsStr List of config methods.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setWpsConfigMethods(String configMethodsStr) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setWpsConfigMethods")) return false;
+ SupplicantResult<Void> result =
+ new SupplicantResult("setWpsConfigMethods(" + configMethodsStr + ")");
+ short configMethodsMask = 0;
+ String[] configMethodsStrArr = configMethodsStr.split("\\s+");
+ for (int i = 0; i < configMethodsStrArr.length; i++) {
+ configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]);
+ }
+ try {
+ result.setResult(mISupplicantP2pIface.setWpsConfigMethods(configMethodsMask));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+ /**
+ * Get NFC handover request message.
+ *
+ * @return select message if created successfully, null otherwise.
+ */
+ public String getNfcHandoverRequest() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverRequest")) return null;
+ SupplicantResult<ArrayList> result = new SupplicantResult(
+ "getNfcHandoverRequest()");
+ try {
+ mISupplicantP2pIface.createNfcHandoverRequestMessage(
+ (SupplicantStatus status, ArrayList<Byte> message) -> {
+ result.setResult(status, message);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ if (!result.isSuccess()) {
+ return null;
+
+ }
+ return NativeUtil.hexStringFromByteArray(
+ NativeUtil.byteArrayFromArrayList(result.getResult()));
+ }
+ }
+
+ /**
+ * Get NFC handover select message.
+ *
+ * @return select message if created successfully, null otherwise.
+ */
+ public String getNfcHandoverSelect() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverSelect")) return null;
+ SupplicantResult<ArrayList> result = new SupplicantResult(
+ "getNfcHandoverSelect()");
+ try {
+ mISupplicantP2pIface.createNfcHandoverSelectMessage(
+ (SupplicantStatus status, ArrayList<Byte> message) -> {
+ result.setResult(status, message);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ if (!result.isSuccess()) {
+ return null;
+
+ }
+ return NativeUtil.hexStringFromByteArray(
+ NativeUtil.byteArrayFromArrayList(result.getResult()));
+ }
+ }
+
+ /**
+ * Report NFC handover select message.
+ *
+ * @return true if reported successfully, false otherwise.
+ */
+ public boolean initiatorReportNfcHandover(String selectMessage) {
+ if (selectMessage == null) return false;
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("initiatorReportNfcHandover")) return false;
+ SupplicantResult<Void> result = new SupplicantResult(
+ "initiatorReportNfcHandover(" + selectMessage + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.reportNfcHandoverInitiation(
+ NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(
+ selectMessage))));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + selectMessage, e);
+ return false;
+ }
+ return result.isSuccess();
+ }
+ }
+
+ /**
+ * Report NFC handover request message.
+ *
+ * @return true if reported successfully, false otherwise.
+ */
+ public boolean responderReportNfcHandover(String requestMessage) {
+ if (requestMessage == null) return false;
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("responderReportNfcHandover")) return false;
+ SupplicantResult<Void> result = new SupplicantResult(
+ "responderReportNfcHandover(" + requestMessage + ")");
+ try {
+ result.setResult(mISupplicantP2pIface.reportNfcHandoverResponse(
+ NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(
+ requestMessage))));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + requestMessage, e);
+ return false;
+ }
+ return result.isSuccess();
+ }
+ }
+
+ /**
+ * Set the client list for the provided network.
+ *
+ * @param networkId Id of the network.
+ * @param clientListStr Space separated list of clients.
+ * @return true, if operation was successful.
+ */
+ public boolean setClientList(int networkId, String clientListStr) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("setClientList")) return false;
+ if (TextUtils.isEmpty(clientListStr)) {
+ Log.e(TAG, "Invalid client list");
+ return false;
+ }
+ ISupplicantP2pNetwork network = getNetwork(networkId);
+ if (network == null) {
+ Log.e(TAG, "Invalid network id ");
+ return false;
+ }
+ SupplicantResult<Void> result = new SupplicantResult(
+ "setClientList(" + networkId + ", " + clientListStr + ")");
+ try {
+ ArrayList<byte[]> clients = new ArrayList<>();
+ for (String clientStr : Arrays.asList(clientListStr.split("\\s+"))) {
+ clients.add(NativeUtil.macAddressToByteArray(clientStr));
+ }
+ result.setResult(network.setClientList(clients));
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument " + clientListStr, e);
+ return false;
+ }
+ return result.isSuccess();
+ }
+ }
+
+ /**
+ * Set the client list for the provided network.
+ *
+ * @param networkId Id of the network.
+ * @return Space separated list of clients if successfull, null otherwise.
+ */
+ public String getClientList(int networkId) {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("getClientList")) return null;
+ ISupplicantP2pNetwork network = getNetwork(networkId);
+ if (network == null) {
+ Log.e(TAG, "Invalid network id ");
+ return null;
+ }
+ SupplicantResult<ArrayList> result = new SupplicantResult(
+ "getClientList(" + networkId + ")");
+ try {
+ network.getClientList(
+ (SupplicantStatus status, ArrayList<byte[]> clients) -> {
+ result.setResult(status, clients);
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ if (!result.isSuccess()) {
+ return null;
+ }
+ ArrayList<byte[]> clients = result.getResult();
+ return clients.stream()
+ .map(NativeUtil::macAddressFromByteArray)
+ .collect(Collectors.joining(" "));
+ }
+ }
+
+ /**
+ * Persist the current configurations to disk.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean saveConfig() {
+ synchronized (mLock) {
+ if (!checkSupplicantP2pIfaceAndLogFailure("saveConfig")) return false;
+ SupplicantResult<Void> result = new SupplicantResult("saveConfig()");
+ try {
+ result.setResult(mISupplicantP2pIface.saveConfig());
+ } catch (RemoteException e) {
+ Log.e(TAG, "ISupplicantP2pIface exception: " + e);
+ supplicantServiceDiedHandler();
+ }
+ return result.isSuccess();
+ }
+ }
+
+ /**
+ * Converts the Wps config method string to the equivalent enum value.
+ */
+ private static short stringToWpsConfigMethod(String configMethod) {
+ switch (configMethod) {
+ case "usba":
+ return WpsConfigMethods.USBA;
+ case "ethernet":
+ return WpsConfigMethods.ETHERNET;
+ case "label":
+ return WpsConfigMethods.LABEL;
+ case "display":
+ return WpsConfigMethods.DISPLAY;
+ case "int_nfc_token":
+ return WpsConfigMethods.INT_NFC_TOKEN;
+ case "ext_nfc_token":
+ return WpsConfigMethods.EXT_NFC_TOKEN;
+ case "nfc_interface":
+ return WpsConfigMethods.NFC_INTERFACE;
+ case "push_button":
+ return WpsConfigMethods.PUSHBUTTON;
+ case "keypad":
+ return WpsConfigMethods.KEYPAD;
+ case "virtual_push_button":
+ return WpsConfigMethods.VIRT_PUSHBUTTON;
+ case "physical_push_button":
+ return WpsConfigMethods.PHY_PUSHBUTTON;
+ case "p2ps":
+ return WpsConfigMethods.P2PS;
+ case "virtual_display":
+ return WpsConfigMethods.VIRT_DISPLAY;
+ case "physical_display":
+ return WpsConfigMethods.PHY_DISPLAY;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid WPS config method: " + configMethod);
+ }
+ }
+
+ /** Container class allowing propagation of status and/or value
+ * from callbacks.
+ *
+ * Primary purpose is to allow callback lambdas to provide results
+ * to parent methods.
+ */
+ private static class SupplicantResult<E> {
+ private String mMethodName;
+ private SupplicantStatus mStatus;
+ private E mValue;
+
+ SupplicantResult(String methodName) {
+ mMethodName = methodName;
+ mStatus = null;
+ mValue = null;
+ logd("entering " + mMethodName);
+ }
+
+ public void setResult(SupplicantStatus status, E value) {
+ logCompletion(mMethodName, status);
+ logd("leaving " + mMethodName + " with result = " + value);
+ mStatus = status;
+ mValue = value;
+ }
+
+ public void setResult(SupplicantStatus status) {
+ logCompletion(mMethodName, status);
+ logd("leaving " + mMethodName);
+ mStatus = status;
+ }
+
+ public boolean isSuccess() {
+ return (mStatus != null && mStatus.code == SupplicantStatusCode.SUCCESS);
+ }
+
+ public E getResult() {
+ return (isSuccess() ? mValue : null);
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java b/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java
new file mode 100644
index 0000000..e14c10c
--- /dev/null
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.p2p;
+
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pProvDiscEvent;
+import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
+import android.os.Handler;
+import android.os.Message;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Protocol;
+import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Listens for events from the wpa_supplicant, and passes them on
+ * to the {@link WifiP2pServiceImpl} for handling.
+ *
+ * @hide
+ */
+public class WifiP2pMonitor {
+ private static final String TAG = "WifiP2pMonitor";
+
+ /* Supplicant events reported to a state machine */
+ private static final int BASE = Protocol.BASE_WIFI_MONITOR;
+
+ /* Connection to supplicant established */
+ public static final int SUP_CONNECTION_EVENT = BASE + 1;
+ /* Connection to supplicant lost */
+ public static final int SUP_DISCONNECTION_EVENT = BASE + 2;
+
+ /* P2P events */
+ public static final int P2P_DEVICE_FOUND_EVENT = BASE + 21;
+ public static final int P2P_DEVICE_LOST_EVENT = BASE + 22;
+ public static final int P2P_GO_NEGOTIATION_REQUEST_EVENT = BASE + 23;
+ public static final int P2P_GO_NEGOTIATION_SUCCESS_EVENT = BASE + 25;
+ public static final int P2P_GO_NEGOTIATION_FAILURE_EVENT = BASE + 26;
+ public static final int P2P_GROUP_FORMATION_SUCCESS_EVENT = BASE + 27;
+ public static final int P2P_GROUP_FORMATION_FAILURE_EVENT = BASE + 28;
+ public static final int P2P_GROUP_STARTED_EVENT = BASE + 29;
+ public static final int P2P_GROUP_REMOVED_EVENT = BASE + 30;
+ public static final int P2P_INVITATION_RECEIVED_EVENT = BASE + 31;
+ public static final int P2P_INVITATION_RESULT_EVENT = BASE + 32;
+ public static final int P2P_PROV_DISC_PBC_REQ_EVENT = BASE + 33;
+ public static final int P2P_PROV_DISC_PBC_RSP_EVENT = BASE + 34;
+ public static final int P2P_PROV_DISC_ENTER_PIN_EVENT = BASE + 35;
+ public static final int P2P_PROV_DISC_SHOW_PIN_EVENT = BASE + 36;
+ public static final int P2P_FIND_STOPPED_EVENT = BASE + 37;
+ public static final int P2P_SERV_DISC_RESP_EVENT = BASE + 38;
+ public static final int P2P_PROV_DISC_FAILURE_EVENT = BASE + 39;
+
+ /* hostap events */
+ public static final int AP_STA_DISCONNECTED_EVENT = BASE + 41;
+ public static final int AP_STA_CONNECTED_EVENT = BASE + 42;
+
+
+ private final WifiInjector mWifiInjector;
+ private boolean mVerboseLoggingEnabled = false;
+ private boolean mConnected = false;
+
+ public WifiP2pMonitor(WifiInjector wifiInjector) {
+ mWifiInjector = wifiInjector;
+ }
+
+ void enableVerboseLogging(int verbose) {
+ if (verbose > 0) {
+ mVerboseLoggingEnabled = true;
+ } else {
+ mVerboseLoggingEnabled = false;
+ }
+ }
+
+ // TODO(b/27569474) remove support for multiple handlers for the same event
+ private final Map<String, SparseArray<Set<Handler>>> mHandlerMap = new HashMap<>();
+
+ /**
+ * Registers a callback handler for the provided event.
+ */
+ public synchronized void registerHandler(String iface, int what, Handler handler) {
+ SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
+ if (ifaceHandlers == null) {
+ ifaceHandlers = new SparseArray<>();
+ mHandlerMap.put(iface, ifaceHandlers);
+ }
+ Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(what);
+ if (ifaceWhatHandlers == null) {
+ ifaceWhatHandlers = new ArraySet<>();
+ ifaceHandlers.put(what, ifaceWhatHandlers);
+ }
+ ifaceWhatHandlers.add(handler);
+ }
+
+ private final Map<String, Boolean> mMonitoringMap = new HashMap<>();
+ private boolean isMonitoring(String iface) {
+ Boolean val = mMonitoringMap.get(iface);
+ if (val == null) {
+ return false;
+ } else {
+ return val.booleanValue();
+ }
+ }
+
+ /**
+ * Enable/Disable monitoring for the provided iface.
+ *
+ * @param iface Name of the iface.
+ * @param enabled true to enable, false to disable.
+ */
+ @VisibleForTesting
+ public void setMonitoring(String iface, boolean enabled) {
+ mMonitoringMap.put(iface, enabled);
+ }
+
+ private void setMonitoringNone() {
+ for (String iface : mMonitoringMap.keySet()) {
+ setMonitoring(iface, false);
+ }
+ }
+
+ /**
+ * Wait for wpa_supplicant's control interface to be ready.
+ *
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
+ private boolean ensureConnectedLocked() {
+ if (mConnected) {
+ return true;
+ }
+ if (mVerboseLoggingEnabled) Log.d(TAG, "connecting to supplicant");
+ int connectTries = 0;
+ while (true) {
+ mConnected = mWifiInjector.getWifiP2pNative().connectToSupplicant();
+ if (mConnected) {
+ return true;
+ }
+ if (connectTries++ < 50) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ignore) {
+ }
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Start Monitoring for wpa_supplicant events.
+ *
+ * @param iface Name of iface.
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
+ public synchronized void startMonitoring(String iface) {
+ if (ensureConnectedLocked()) {
+ setMonitoring(iface, true);
+ broadcastSupplicantConnectionEvent(iface);
+ } else {
+ boolean originalMonitoring = isMonitoring(iface);
+ setMonitoring(iface, true);
+ broadcastSupplicantDisconnectionEvent(iface);
+ setMonitoring(iface, originalMonitoring);
+ Log.e(TAG, "startMonitoring(" + iface + ") failed!");
+ }
+ }
+
+ /**
+ * Stop Monitoring for wpa_supplicant events.
+ *
+ * @param iface Name of iface.
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
+ public synchronized void stopMonitoring(String iface) {
+ if (mVerboseLoggingEnabled) Log.d(TAG, "stopMonitoring(" + iface + ")");
+ setMonitoring(iface, true);
+ broadcastSupplicantDisconnectionEvent(iface);
+ setMonitoring(iface, false);
+ }
+
+ /**
+ * Stop Monitoring for wpa_supplicant events.
+ *
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
+ public synchronized void stopAllMonitoring() {
+ mConnected = false;
+ setMonitoringNone();
+ }
+
+ /**
+ * Similar functions to Handler#sendMessage that send the message to the registered handler
+ * for the given interface and message what.
+ * All of these should be called with the WifiMonitor class lock
+ */
+ private void sendMessage(String iface, int what) {
+ sendMessage(iface, Message.obtain(null, what));
+ }
+
+ private void sendMessage(String iface, int what, Object obj) {
+ sendMessage(iface, Message.obtain(null, what, obj));
+ }
+
+ private void sendMessage(String iface, int what, int arg1) {
+ sendMessage(iface, Message.obtain(null, what, arg1, 0));
+ }
+
+ private void sendMessage(String iface, int what, int arg1, int arg2) {
+ sendMessage(iface, Message.obtain(null, what, arg1, arg2));
+ }
+
+ private void sendMessage(String iface, int what, int arg1, int arg2, Object obj) {
+ sendMessage(iface, Message.obtain(null, what, arg1, arg2, obj));
+ }
+
+ private void sendMessage(String iface, Message message) {
+ SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface);
+ if (iface != null && ifaceHandlers != null) {
+ if (isMonitoring(iface)) {
+ Set<Handler> ifaceWhatHandlers = ifaceHandlers.get(message.what);
+ if (ifaceWhatHandlers != null) {
+ for (Handler handler : ifaceWhatHandlers) {
+ if (handler != null) {
+ sendMessage(handler, Message.obtain(message));
+ }
+ }
+ }
+ } else {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Dropping event because (" + iface + ") is stopped");
+ }
+ }
+ } else {
+ if (mVerboseLoggingEnabled) {
+ Log.d(TAG, "Sending to all monitors because there's no matching iface");
+ }
+ for (Map.Entry<String, SparseArray<Set<Handler>>> entry : mHandlerMap.entrySet()) {
+ if (isMonitoring(entry.getKey())) {
+ Set<Handler> ifaceWhatHandlers = entry.getValue().get(message.what);
+ for (Handler handler : ifaceWhatHandlers) {
+ if (handler != null) {
+ sendMessage(handler, Message.obtain(message));
+ }
+ }
+ }
+ }
+ }
+
+ message.recycle();
+ }
+
+ private void sendMessage(Handler handler, Message message) {
+ message.setTarget(handler);
+ message.sendToTarget();
+ }
+
+ /**
+ * Broadcast the connection to wpa_supplicant event to all the handlers registered for
+ * this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastSupplicantConnectionEvent(String iface) {
+ sendMessage(iface, SUP_CONNECTION_EVENT);
+ }
+
+ /**
+ * Broadcast the loss of connection to wpa_supplicant event to all the handlers registered for
+ * this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastSupplicantDisconnectionEvent(String iface) {
+ sendMessage(iface, SUP_DISCONNECTION_EVENT);
+ }
+
+ /**
+ * Broadcast new p2p device discovered event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param device Device that has been discovered during recent scan.
+ */
+ public void broadcastP2pDeviceFound(String iface, WifiP2pDevice device) {
+ if (device != null) {
+ sendMessage(iface, P2P_DEVICE_FOUND_EVENT, device);
+ }
+ }
+
+ /**
+ * Broadcast p2p device lost event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param device Device that has been lost in recent scan.
+ */
+ public void broadcastP2pDeviceLost(String iface, WifiP2pDevice device) {
+ if (device != null) {
+ sendMessage(iface, P2P_DEVICE_LOST_EVENT, device);
+ }
+ }
+
+ /**
+ * Broadcast scan termination event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastP2pFindStopped(String iface) {
+ sendMessage(iface, P2P_FIND_STOPPED_EVENT);
+ }
+
+ /**
+ * Broadcast group owner negotiation request event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param config P2p configuration.
+ */
+ public void broadcastP2pGoNegotiationRequest(String iface, WifiP2pConfig config) {
+ if (config != null) {
+ sendMessage(iface, P2P_GO_NEGOTIATION_REQUEST_EVENT, config);
+ }
+ }
+
+ /**
+ * Broadcast group owner negotiation success event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastP2pGoNegotiationSuccess(String iface) {
+ sendMessage(iface, P2P_GO_NEGOTIATION_SUCCESS_EVENT);
+ }
+
+ /**
+ * Broadcast group owner negotiation failure event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param reason Failure reason.
+ */
+ public void broadcastP2pGoNegotiationFailure(String iface, P2pStatus reason) {
+ sendMessage(iface, P2P_GO_NEGOTIATION_FAILURE_EVENT, reason);
+ }
+
+ /**
+ * Broadcast group formation success event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastP2pGroupFormationSuccess(String iface) {
+ sendMessage(iface, P2P_GROUP_FORMATION_SUCCESS_EVENT);
+ }
+
+ /**
+ * Broadcast group formation failure event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param reason Failure reason.
+ */
+ public void broadcastP2pGroupFormationFailure(String iface, String reason) {
+ P2pStatus err = P2pStatus.UNKNOWN;
+ if (reason.equals("FREQ_CONFLICT")) {
+ err = P2pStatus.NO_COMMON_CHANNEL;
+ }
+ sendMessage(iface, P2P_GROUP_FORMATION_FAILURE_EVENT, err);
+ }
+
+ /**
+ * Broadcast group started event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param group Started group.
+ */
+ public void broadcastP2pGroupStarted(String iface, WifiP2pGroup group) {
+ if (group != null) {
+ sendMessage(iface, P2P_GROUP_STARTED_EVENT, group);
+ }
+ }
+
+ /**
+ * Broadcast group removed event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param group Removed group.
+ */
+ public void broadcastP2pGroupRemoved(String iface, WifiP2pGroup group) {
+ if (group != null) {
+ sendMessage(iface, P2P_GROUP_REMOVED_EVENT, group);
+ }
+ }
+
+ /**
+ * Broadcast invitation received event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param group Group to which invitation has been received.
+ */
+ public void broadcastP2pInvitationReceived(String iface, WifiP2pGroup group) {
+ if (group != null) {
+ sendMessage(iface, P2P_INVITATION_RECEIVED_EVENT, group);
+ }
+ }
+
+ /**
+ * Broadcast invitation result event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param result Result of invitation.
+ */
+ public void broadcastP2pInvitationResult(String iface, P2pStatus result) {
+ sendMessage(iface, P2P_INVITATION_RESULT_EVENT, result);
+ }
+
+ /**
+ * Broadcast PB discovery request event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param event Provision discovery request event.
+ */
+ public void broadcastP2pProvisionDiscoveryPbcRequest(String iface, WifiP2pProvDiscEvent event) {
+ if (event != null) {
+ sendMessage(iface, P2P_PROV_DISC_PBC_REQ_EVENT, event);
+ }
+ }
+
+ /**
+ * Broadcast PB discovery response event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param event Provision discovery response event.
+ */
+ public void broadcastP2pProvisionDiscoveryPbcResponse(
+ String iface, WifiP2pProvDiscEvent event) {
+ if (event != null) {
+ sendMessage(iface, P2P_PROV_DISC_PBC_RSP_EVENT, event);
+ }
+ }
+
+ /**
+ * Broadcast PIN discovery request event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param event Provision discovery request event.
+ */
+ public void broadcastP2pProvisionDiscoveryEnterPin(String iface, WifiP2pProvDiscEvent event) {
+ if (event != null) {
+ sendMessage(iface, P2P_PROV_DISC_ENTER_PIN_EVENT, event);
+ }
+ }
+
+ /**
+ * Broadcast PIN discovery response event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param event Provision discovery response event.
+ */
+ public void broadcastP2pProvisionDiscoveryShowPin(String iface, WifiP2pProvDiscEvent event) {
+ if (event != null) {
+ sendMessage(iface, P2P_PROV_DISC_SHOW_PIN_EVENT, event);
+ }
+ }
+
+ /**
+ * Broadcast P2P discovery failure event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastP2pProvisionDiscoveryFailure(String iface) {
+ sendMessage(iface, P2P_PROV_DISC_FAILURE_EVENT);
+ }
+
+ /**
+ * Broadcast service discovery response event to all handlers registered for this event.
+ *
+ * @param iface Name of iface on which this occurred.
+ * @param services List of discovered services.
+ */
+ public void broadcastP2pServiceDiscoveryResponse(
+ String iface, List<WifiP2pServiceResponse> services) {
+ sendMessage(iface, P2P_SERV_DISC_RESP_EVENT, services);
+ }
+
+ /**
+ * Broadcast AP STA connection event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastP2pApStaConnected(String iface, WifiP2pDevice device) {
+ sendMessage(iface, AP_STA_CONNECTED_EVENT, device);
+ }
+
+ /**
+ * Broadcast AP STA disconnection event.
+ *
+ * @param iface Name of iface on which this occurred.
+ */
+ public void broadcastP2pApStaDisconnected(String iface, WifiP2pDevice device) {
+ sendMessage(iface, AP_STA_DISCONNECTED_EVENT, device);
+ }
+}
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pNative.java b/service/java/com/android/server/wifi/p2p/WifiP2pNative.java
new file mode 100644
index 0000000..bae3faa
--- /dev/null
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pNative.java
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.p2p;
+
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pGroupList;
+import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+
+/**
+ * Native calls for bring up/shut down of the supplicant daemon and for
+ * sending requests to the supplicant daemon
+ *
+ * {@hide}
+ */
+public class WifiP2pNative {
+ private final String mTAG;
+ private final String mInterfaceName;
+ private final SupplicantP2pIfaceHal mSupplicantP2pIfaceHal;
+
+ public WifiP2pNative(String interfaceName, SupplicantP2pIfaceHal p2pIfaceHal) {
+ mTAG = "WifiP2pNative-" + interfaceName;
+ mInterfaceName = interfaceName;
+ mSupplicantP2pIfaceHal = p2pIfaceHal;
+ }
+
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
+ /**
+ * Enable verbose logging for all sub modules.
+ */
+ public void enableVerboseLogging(int verbose) {
+ }
+
+ /********************************************************
+ * Supplicant operations
+ ********************************************************/
+ /**
+ * This method is called repeatedly until the connection to wpa_supplicant is established.
+ *
+ * @return true if connection is established, false otherwise.
+ * TODO: Add unit tests for these once we remove the legacy code.
+ */
+ public boolean connectToSupplicant() {
+ // Start initialization if not already started.
+ if (!mSupplicantP2pIfaceHal.isInitializationStarted()
+ && !mSupplicantP2pIfaceHal.initialize()) {
+ return false;
+ }
+ // Check if the initialization is complete.
+ return mSupplicantP2pIfaceHal.isInitializationComplete();
+ }
+
+ /**
+ * Close supplicant connection.
+ */
+ public void closeSupplicantConnection() {
+ // Nothing to do for HIDL.
+ }
+
+ /**
+ * Set WPS device name.
+ *
+ * @param name String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setDeviceName(String name) {
+ return mSupplicantP2pIfaceHal.setWpsDeviceName(name);
+ }
+
+ /**
+ * Populate list of available networks or update existing list.
+ *
+ * @return true, if list has been modified.
+ */
+ public boolean p2pListNetworks(WifiP2pGroupList groups) {
+ return mSupplicantP2pIfaceHal.loadGroups(groups);
+ }
+
+ /**
+ * Initiate WPS Push Button setup.
+ * The PBC operation requires that a button is also pressed at the
+ * AP/Registrar at about the same time (2 minute window).
+ *
+ * @param iface Group interface name to use.
+ * @param bssid BSSID of the AP. Use zero'ed bssid to indicate wildcard.
+ * @return true, if operation was successful.
+ */
+ public boolean startWpsPbc(String iface, String bssid) {
+ return mSupplicantP2pIfaceHal.startWpsPbc(iface, bssid);
+ }
+
+ /**
+ * Initiate WPS Pin Keypad setup.
+ *
+ * @param iface Group interface name to use.
+ * @param pin 8 digit pin to be used.
+ * @return true, if operation was successful.
+ */
+ public boolean startWpsPinKeypad(String iface, String pin) {
+ return mSupplicantP2pIfaceHal.startWpsPinKeypad(iface, pin);
+ }
+
+ /**
+ * Initiate WPS Pin Display setup.
+ *
+ * @param iface Group interface name to use.
+ * @param bssid BSSID of the AP. Use zero'ed bssid to indicate wildcard.
+ * @return generated pin if operation was successful, null otherwise.
+ */
+ public String startWpsPinDisplay(String iface, String bssid) {
+ return mSupplicantP2pIfaceHal.startWpsPinDisplay(iface, bssid);
+ }
+
+ /**
+ * Remove network with provided id.
+ *
+ * @param netId Id of the network to lookup.
+ * @return true, if operation was successful.
+ */
+ public boolean removeP2pNetwork(int netId) {
+ return mSupplicantP2pIfaceHal.removeNetwork(netId);
+ }
+
+ /**
+ * Set WPS device name.
+ *
+ * @param name String to be set.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setP2pDeviceName(String name) {
+ return mSupplicantP2pIfaceHal.setWpsDeviceName(name);
+ }
+
+ /**
+ * Set WPS device type.
+ *
+ * @param type Type specified as a string. Used format: <categ>-<OUI>-<subcateg>
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setP2pDeviceType(String type) {
+ return mSupplicantP2pIfaceHal.setWpsDeviceType(type);
+ }
+
+ /**
+ * Set WPS config methods
+ *
+ * @param cfg List of config methods.
+ * @return true if request is sent successfully, false otherwise.
+ */
+ public boolean setConfigMethods(String cfg) {
+ return mSupplicantP2pIfaceHal.setWpsConfigMethods(cfg);
+ }
+
+ /**
+ * Set the postfix to be used for P2P SSID's.
+ *
+ * @param postfix String to be appended to SSID.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean setP2pSsidPostfix(String postfix) {
+ return mSupplicantP2pIfaceHal.setSsidPostfix(postfix);
+ }
+
+ /**
+ * Set the Maximum idle time in seconds for P2P groups.
+ * This value controls how long a P2P group is maintained after there
+ * is no other members in the group. As a group owner, this means no
+ * associated stations in the group. As a P2P client, this means no
+ * group owner seen in scan results.
+ *
+ * @param iface Group interface name to use.
+ * @param time Timeout value in seconds.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean setP2pGroupIdle(String iface, int time) {
+ return mSupplicantP2pIfaceHal.setGroupIdle(iface, time);
+ }
+
+ /**
+ * Turn on/off power save mode for the interface.
+ *
+ * @param iface Group interface name to use.
+ * @param enabled Indicate if power save is to be turned on/off.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean setP2pPowerSave(String iface, boolean enabled) {
+ return mSupplicantP2pIfaceHal.setPowerSave(iface, enabled);
+ }
+
+ /**
+ * Enable/Disable Wifi Display.
+ *
+ * @param enable true to enable, false to disable.
+ * @return true, if operation was successful.
+ */
+ public boolean setWfdEnable(boolean enable) {
+ return mSupplicantP2pIfaceHal.enableWfd(enable);
+ }
+
+ /**
+ * Set Wifi Display device info.
+ *
+ * @param hex WFD device info as described in section 5.1.2 of WFD technical
+ * specification v1.0.0.
+ * @return true, if operation was successful.
+ */
+ public boolean setWfdDeviceInfo(String hex) {
+ return mSupplicantP2pIfaceHal.setWfdDeviceInfo(hex);
+ }
+
+ /**
+ * Initiate a P2P service discovery indefinitely.
+ * Will trigger {@link WifiP2pMonitor#P2P_DEVICE_FOUND_EVENT} on finding devices.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean p2pFind() {
+ return p2pFind(0);
+ }
+
+ /**
+ * Initiate a P2P service discovery with a (optional) timeout.
+ *
+ * @param timeout Max time to be spent is peforming discovery.
+ * Set to 0 to indefinely continue discovery untill and explicit
+ * |stopFind| is sent.
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean p2pFind(int timeout) {
+ return mSupplicantP2pIfaceHal.find(timeout);
+ }
+
+ /**
+ * Stop an ongoing P2P service discovery.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean p2pStopFind() {
+ return mSupplicantP2pIfaceHal.stopFind();
+ }
+
+ /**
+ * Configure Extended Listen Timing.
+ *
+ * If enabled, listen state must be entered every |intervalInMillis| for at
+ * least |periodInMillis|. Both values have acceptable range of 1-65535
+ * (with interval obviously having to be larger than or equal to duration).
+ * If the P2P module is not idle at the time the Extended Listen Timing
+ * timeout occurs, the Listen State operation must be skipped.
+ *
+ * @param enable Enables or disables listening.
+ * @param period Period in milliseconds.
+ * @param interval Interval in milliseconds.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean p2pExtListen(boolean enable, int period, int interval) {
+ return mSupplicantP2pIfaceHal.configureExtListen(enable, period, interval);
+ }
+
+ /**
+ * Set P2P Listen channel.
+ *
+ * When specifying a social channel on the 2.4 GHz band (1/6/11) there is no
+ * need to specify the operating class since it defaults to 81. When
+ * specifying a social channel on the 60 GHz band (2), specify the 60 GHz
+ * operating class (180).
+ *
+ * @param lc Wifi channel. eg, 1, 6, 11.
+ * @param oc Operating Class indicates the channel set of the AP
+ * indicated by this BSSID
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean p2pSetChannel(int lc, int oc) {
+ return mSupplicantP2pIfaceHal.setListenChannel(lc, oc);
+ }
+
+ /**
+ * Flush P2P peer table and state.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean p2pFlush() {
+ return mSupplicantP2pIfaceHal.flush();
+ }
+
+ /**
+ * Start P2P group formation with a discovered P2P peer. This includes
+ * optional group owner negotiation, group interface setup, provisioning,
+ * and establishing data connection.
+ *
+ * @param config Configuration to use to connect to remote device.
+ * @param joinExistingGroup Indicates that this is a command to join an
+ * existing group as a client. It skips the group owner negotiation
+ * part. This must send a Provision Discovery Request message to the
+ * target group owner before associating for WPS provisioning.
+ *
+ * @return String containing generated pin, if selected provision method
+ * uses PIN.
+ */
+ public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
+ return mSupplicantP2pIfaceHal.connect(config, joinExistingGroup);
+ }
+
+ /**
+ * Cancel an ongoing P2P group formation and joining-a-group related
+ * operation. This operation unauthorizes the specific peer device (if any
+ * had been authorized to start group formation), stops P2P find (if in
+ * progress), stops pending operations for join-a-group, and removes the
+ * P2P group interface (if one was used) that is in the WPS provisioning
+ * step. If the WPS provisioning step has been completed, the group is not
+ * terminated.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean p2pCancelConnect() {
+ return mSupplicantP2pIfaceHal.cancelConnect();
+ }
+
+ /**
+ * Send P2P provision discovery request to the specified peer. The
+ * parameters for this command are the P2P device address of the peer and the
+ * desired configuration method.
+ *
+ * @param config Config class describing peer setup.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
+ return mSupplicantP2pIfaceHal.provisionDiscovery(config);
+ }
+
+ /**
+ * Set up a P2P group owner manually.
+ * This is a helper method that invokes groupAdd(networkId, isPersistent) internally.
+ *
+ * @param persistent Used to request a persistent group to be formed.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean p2pGroupAdd(boolean persistent) {
+ return mSupplicantP2pIfaceHal.groupAdd(persistent);
+ }
+
+ /**
+ * Set up a P2P group owner manually (i.e., without group owner
+ * negotiation with a specific peer). This is also known as autonomous
+ * group owner.
+ *
+ * @param netId Used to specify the restart of a persistent group.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean p2pGroupAdd(int netId) {
+ return mSupplicantP2pIfaceHal.groupAdd(netId, true);
+ }
+
+ /**
+ * Terminate a P2P group. If a new virtual network interface was used for
+ * the group, it must also be removed. The network interface name of the
+ * group interface is used as a parameter for this command.
+ *
+ * @param iface Group interface name to use.
+ * @return true, if operation was successful.
+ */
+ public boolean p2pGroupRemove(String iface) {
+ return mSupplicantP2pIfaceHal.groupRemove(iface);
+ }
+
+ /**
+ * Reject connection attempt from a peer (specified with a device
+ * address). This is a mechanism to reject a pending group owner negotiation
+ * with a peer and request to automatically block any further connection or
+ * discovery of the peer.
+ *
+ * @param deviceAddress MAC address of the device to reject.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean p2pReject(String deviceAddress) {
+ return mSupplicantP2pIfaceHal.reject(deviceAddress);
+ }
+
+ /**
+ * Invite a device to a persistent group.
+ * If the peer device is the group owner of the persistent group, the peer
+ * parameter is not needed. Otherwise it is used to specify which
+ * device to invite. |goDeviceAddress| parameter may be used to override
+ * the group owner device address for Invitation Request should it not be
+ * known for some reason (this should not be needed in most cases).
+ *
+ * @param group Group object to use.
+ * @param deviceAddress MAC address of the device to invite.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
+ return mSupplicantP2pIfaceHal.invite(group, deviceAddress);
+ }
+
+ /**
+ * Reinvoke a device from a persistent group.
+ *
+ * @param netId Used to specify the persistent group.
+ * @param deviceAddress MAC address of the device to reinvoke.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean p2pReinvoke(int netId, String deviceAddress) {
+ return mSupplicantP2pIfaceHal.reinvoke(netId, deviceAddress);
+ }
+
+ /**
+ * Gets the operational SSID of the device.
+ *
+ * @param deviceAddress MAC address of the peer.
+ *
+ * @return SSID of the device.
+ */
+ public String p2pGetSsid(String deviceAddress) {
+ return mSupplicantP2pIfaceHal.getSsid(deviceAddress);
+ }
+
+ /**
+ * Gets the MAC address of the device.
+ *
+ * @return MAC address of the device.
+ */
+ public String p2pGetDeviceAddress() {
+ return mSupplicantP2pIfaceHal.getDeviceAddress();
+ }
+
+ /**
+ * Gets the capability of the group which the device is a
+ * member of.
+ *
+ * @param deviceAddress MAC address of the peer.
+ *
+ * @return combination of |GroupCapabilityMask| values.
+ */
+ public int getGroupCapability(String deviceAddress) {
+ return mSupplicantP2pIfaceHal.getGroupCapability(deviceAddress);
+ }
+
+ /**
+ * This command can be used to add a upnp/bonjour service.
+ *
+ * @param servInfo List of service queries.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
+ return mSupplicantP2pIfaceHal.serviceAdd(servInfo);
+ }
+
+ /**
+ * This command can be used to remove a upnp/bonjour service.
+ *
+ * @param servInfo List of service queries.
+ *
+ * @return true, if operation was successful.
+ */
+ public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
+ return mSupplicantP2pIfaceHal.serviceRemove(servInfo);
+ }
+
+ /**
+ * This command can be used to flush all services from the
+ * device.
+ *
+ * @return boolean value indicating whether operation was successful.
+ */
+ public boolean p2pServiceFlush() {
+ return mSupplicantP2pIfaceHal.serviceFlush();
+ }
+
+ /**
+ * Schedule a P2P service discovery request. The parameters for this command
+ * are the device address of the peer device (or 00:00:00:00:00:00 for
+ * wildcard query that is sent to every discovered P2P peer that supports
+ * service discovery) and P2P Service Query TLV(s) as hexdump.
+ *
+ * @param addr MAC address of the device to discover.
+ * @param query Hex dump of the query data.
+ * @return identifier Identifier for the request. Can be used to cancel the
+ * request.
+ */
+ public String p2pServDiscReq(String addr, String query) {
+ return mSupplicantP2pIfaceHal.requestServiceDiscovery(addr, query);
+ }
+
+ /**
+ * Cancel a previous service discovery request.
+ *
+ * @param id Identifier for the request to cancel.
+ * @return true, if operation was successful.
+ */
+ public boolean p2pServDiscCancelReq(String id) {
+ return mSupplicantP2pIfaceHal.cancelServiceDiscovery(id);
+ }
+
+ /**
+ * Send driver command to set Miracast mode.
+ *
+ * @param mode Mode of Miracast.
+ * 0 = disabled
+ * 1 = operating as source
+ * 2 = operating as sink
+ */
+ public void setMiracastMode(int mode) {
+ mSupplicantP2pIfaceHal.setMiracastMode(mode);
+ }
+
+ /**
+ * Get NFC handover request message.
+ *
+ * @return select message if created successfully, null otherwise.
+ */
+ public String getNfcHandoverRequest() {
+ return mSupplicantP2pIfaceHal.getNfcHandoverRequest();
+ }
+
+ /**
+ * Get NFC handover select message.
+ *
+ * @return select message if created successfully, null otherwise.
+ */
+ public String getNfcHandoverSelect() {
+ return mSupplicantP2pIfaceHal.getNfcHandoverSelect();
+ }
+
+ /**
+ * Report NFC handover select message.
+ *
+ * @return true if reported successfully, false otherwise.
+ */
+ public boolean initiatorReportNfcHandover(String selectMessage) {
+ return mSupplicantP2pIfaceHal.initiatorReportNfcHandover(selectMessage);
+ }
+
+ /**
+ * Report NFC handover request message.
+ *
+ * @return true if reported successfully, false otherwise.
+ */
+ public boolean responderReportNfcHandover(String requestMessage) {
+ return mSupplicantP2pIfaceHal.responderReportNfcHandover(requestMessage);
+ }
+
+ /**
+ * Set the client list for the provided network.
+ *
+ * @param netId Id of the network.
+ * @return Space separated list of clients if successfull, null otherwise.
+ */
+ public String getP2pClientList(int netId) {
+ return mSupplicantP2pIfaceHal.getClientList(netId);
+ }
+
+ /**
+ * Set the client list for the provided network.
+ *
+ * @param netId Id of the network.
+ * @param list Space separated list of clients.
+ * @return true, if operation was successful.
+ */
+ public boolean setP2pClientList(int netId, String list) {
+ return mSupplicantP2pIfaceHal.setClientList(netId, list);
+ }
+
+ /**
+ * Save the current configuration to p2p_supplicant.conf.
+ *
+ * @return true on success, false otherwise.
+ */
+ public boolean saveConfig() {
+ return mSupplicantP2pIfaceHal.saveConfig();
+ }
+}
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pService.java b/service/java/com/android/server/wifi/p2p/WifiP2pService.java
index dacde3a..97c0189 100644
--- a/service/java/com/android/server/wifi/p2p/WifiP2pService.java
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pService.java
@@ -18,8 +18,14 @@
import android.content.Context;
import android.util.Log;
+
import com.android.server.SystemService;
+/**
+ * Wifi P2p Service class, instantiates P2p service
+ * Overrides onStart() and onBootPhase() methods in
+ * the super class.
+ */
public final class WifiP2pService extends SystemService {
private static final String TAG = "WifiP2pService";
diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
index c1d9445..623a2f0 100644
--- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
+++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java
@@ -48,8 +48,8 @@
import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -61,6 +61,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.view.KeyEvent;
@@ -76,9 +77,12 @@
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import com.android.server.wifi.WifiMonitor;
-import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.WifiInjector;
import com.android.server.wifi.WifiStateMachine;
+import com.android.server.wifi.util.WifiAsyncChannel;
+import com.android.server.wifi.util.WifiHandler;
+import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -88,7 +92,6 @@
import java.util.HashMap;
import java.util.List;
-
/**
* WifiP2pService includes a state machine to perform Wi-Fi p2p operations. Applications
* communicate with this service to issue device discovery and connectivity requests
@@ -111,8 +114,9 @@
private DhcpResults mDhcpResults;
private P2pStateMachine mP2pStateMachine;
- private AsyncChannel mReplyChannel = new AsyncChannel();
+ private AsyncChannel mReplyChannel = new WifiAsyncChannel(TAG);
private AsyncChannel mWifiChannel;
+ private WifiInjector mWifiInjector;
private static final Boolean JOIN_GROUP = true;
private static final Boolean FORM_GROUP = false;
@@ -120,50 +124,47 @@
private static final Boolean RELOAD = true;
private static final Boolean NO_RELOAD = false;
- /* Two minutes comes from the wpa_supplicant setting */
+ // Two minutes comes from the wpa_supplicant setting
private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
- private static int mGroupCreatingTimeoutIndex = 0;
+ private static int sGroupCreatingTimeoutIndex = 0;
private static final int DISABLE_P2P_WAIT_TIME_MS = 5 * 1000;
- private static int mDisableP2pTimeoutIndex = 0;
+ private static int sDisableP2pTimeoutIndex = 0;
- /* Set a two minute discover timeout to avoid STA scans from being blocked */
+ // Set a two minute discover timeout to avoid STA scans from being blocked
private static final int DISCOVER_TIMEOUT_S = 120;
- /* Idle time after a peer is gone when the group is torn down */
+ // Idle time after a peer is gone when the group is torn down
private static final int GROUP_IDLE_TIME_S = 10;
private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
- /* Delayed message to timeout group creation */
+ // Delayed message to timeout group creation
public static final int GROUP_CREATING_TIMED_OUT = BASE + 1;
- /* User accepted a peer request */
+ // User accepted a peer request
private static final int PEER_CONNECTION_USER_ACCEPT = BASE + 2;
- /* User rejected a peer request */
+ // User rejected a peer request
private static final int PEER_CONNECTION_USER_REJECT = BASE + 3;
- /* User wants to disconnect wifi in favour of p2p */
+ // User wants to disconnect wifi in favour of p2p
private static final int DROP_WIFI_USER_ACCEPT = BASE + 4;
- /* User wants to keep his wifi connection and drop p2p */
+ // User wants to keep his wifi connection and drop p2p
private static final int DROP_WIFI_USER_REJECT = BASE + 5;
- /* Delayed message to timeout p2p disable */
+ // Delayed message to timeout p2p disable
public static final int DISABLE_P2P_TIMED_OUT = BASE + 6;
- /* Commands to the WifiStateMachine */
+ // Commands to the WifiStateMachine
public static final int P2P_CONNECTION_CHANGED = BASE + 11;
- /* These commands are used to temporarily disconnect wifi when we detect
- * a frequency conflict which would make it impossible to have with p2p
- * and wifi active at the same time.
- *
- * If the user chooses to disable wifi temporarily, we keep wifi disconnected
- * until the p2p connection is done and terminated at which point we will
- * bring back wifi up
- *
- * DISCONNECT_WIFI_REQUEST
- * msg.arg1 = 1 enables temporary disconnect and 0 disables it.
- */
+ // These commands are used to temporarily disconnect wifi when we detect
+ // a frequency conflict which would make it impossible to have with p2p
+ // and wifi active at the same time.
+ // If the user chooses to disable wifi temporarily, we keep wifi disconnected
+ // until the p2p connection is done and terminated at which point we will
+ // bring back wifi up
+ // DISCONNECT_WIFI_REQUEST
+ // msg.arg1 = 1 enables temporary disconnect and 0 disables it.
public static final int DISCONNECT_WIFI_REQUEST = BASE + 12;
public static final int DISCONNECT_WIFI_RESPONSE = BASE + 13;
@@ -190,120 +191,122 @@
private WifiP2pDevice mThisDevice = new WifiP2pDevice();
- /* When a group has been explicitly created by an app, we persist the group
- * even after all clients have been disconnected until an explicit remove
- * is invoked */
+ // When a group has been explicitly created by an app, we persist the group
+ // even after all clients have been disconnected until an explicit remove
+ // is invoked
private boolean mAutonomousGroup;
- /* Invitation to join an existing p2p group */
+ // Invitation to join an existing p2p group
private boolean mJoinExistingGroup;
- /* Track whether we are in p2p discovery. This is used to avoid sending duplicate
- * broadcasts
- */
+ // Track whether we are in p2p discovery. This is used to avoid sending duplicate
+ // broadcasts
private boolean mDiscoveryStarted;
- /* Track whether servcice/peer discovery is blocked in favor of other wifi actions
- * (notably dhcp)
- */
+
+ // Track whether servcice/peer discovery is blocked in favor of other wifi actions
+ // (notably dhcp)
private boolean mDiscoveryBlocked;
- /*
- * remember if we were in a scan when it had to be stopped
- */
+ // remember if we were in a scan when it had to be stopped
private boolean mDiscoveryPostponed = false;
private NetworkInfo mNetworkInfo;
private boolean mTemporarilyDisconnectedWifi = false;
- /* The transaction Id of service discovery request */
+ // The transaction Id of service discovery request
private byte mServiceTransactionId = 0;
- /* Service discovery request ID of wpa_supplicant.
- * null means it's not set yet. */
+ // Service discovery request ID of wpa_supplicant.
+ // null means it's not set yet.
private String mServiceDiscReqId;
- /* clients(application) information list. */
+ // clients(application) information list
private HashMap<Messenger, ClientInfo> mClientInfoList = new HashMap<Messenger, ClientInfo>();
- /* Is chosen as a unique address to avoid conflict with
- the ranges defined in Tethering.java */
+ // Is chosen as a unique address to avoid conflict with
+ // the ranges defined in Tethering.java
private static final String SERVER_ADDRESS = "192.168.49.1";
/**
* Error code definition.
* see the Table.8 in the WiFi Direct specification for the detail.
*/
- public static enum P2pStatus {
- /* Success. */
+ public enum P2pStatus {
+ // Success
SUCCESS,
- /* The target device is currently unavailable. */
+ // The target device is currently unavailable
INFORMATION_IS_CURRENTLY_UNAVAILABLE,
- /* Protocol error. */
+ // Protocol error
INCOMPATIBLE_PARAMETERS,
- /* The target device reached the limit of the number of the connectable device.
- * For example, device limit or group limit is set. */
+ // The target device reached the limit of the number of the connectable device.
+ // For example, device limit or group limit is set
LIMIT_REACHED,
- /* Protocol error. */
+ // Protocol error
INVALID_PARAMETER,
- /* Unable to accommodate request. */
+ // Unable to accommodate request
UNABLE_TO_ACCOMMODATE_REQUEST,
- /* Previous protocol error, or disruptive behavior. */
+ // Previous protocol error, or disruptive behavior
PREVIOUS_PROTOCOL_ERROR,
- /* There is no common channels the both devices can use. */
+ // There is no common channels the both devices can use
NO_COMMON_CHANNEL,
- /* Unknown p2p group. For example, Device A tries to invoke the previous persistent group,
- * but device B has removed the specified credential already. */
+ // Unknown p2p group. For example, Device A tries to invoke the previous persistent group,
+ // but device B has removed the specified credential already
UNKNOWN_P2P_GROUP,
- /* Both p2p devices indicated an intent of 15 in group owner negotiation. */
+ // Both p2p devices indicated an intent of 15 in group owner negotiation
BOTH_GO_INTENT_15,
- /* Incompatible provisioning method. */
+ // Incompatible provisioning method
INCOMPATIBLE_PROVISIONING_METHOD,
- /* Rejected by user */
+ // Rejected by user
REJECTED_BY_USER,
- /* Unknown error */
+ // Unknown error
UNKNOWN;
+ /**
+ * Returns P2p status corresponding to a given error value
+ * @param error integer error value
+ * @return P2pStatus enum for value
+ */
public static P2pStatus valueOf(int error) {
switch(error) {
- case 0 :
- return SUCCESS;
- case 1:
- return INFORMATION_IS_CURRENTLY_UNAVAILABLE;
- case 2:
- return INCOMPATIBLE_PARAMETERS;
- case 3:
- return LIMIT_REACHED;
- case 4:
- return INVALID_PARAMETER;
- case 5:
- return UNABLE_TO_ACCOMMODATE_REQUEST;
- case 6:
- return PREVIOUS_PROTOCOL_ERROR;
- case 7:
- return NO_COMMON_CHANNEL;
- case 8:
- return UNKNOWN_P2P_GROUP;
- case 9:
- return BOTH_GO_INTENT_15;
- case 10:
- return INCOMPATIBLE_PROVISIONING_METHOD;
- case 11:
- return REJECTED_BY_USER;
- default:
- return UNKNOWN;
+ case 0 :
+ return SUCCESS;
+ case 1:
+ return INFORMATION_IS_CURRENTLY_UNAVAILABLE;
+ case 2:
+ return INCOMPATIBLE_PARAMETERS;
+ case 3:
+ return LIMIT_REACHED;
+ case 4:
+ return INVALID_PARAMETER;
+ case 5:
+ return UNABLE_TO_ACCOMMODATE_REQUEST;
+ case 6:
+ return PREVIOUS_PROTOCOL_ERROR;
+ case 7:
+ return NO_COMMON_CHANNEL;
+ case 8:
+ return UNKNOWN_P2P_GROUP;
+ case 9:
+ return BOTH_GO_INTENT_15;
+ case 10:
+ return INCOMPATIBLE_PROVISIONING_METHOD;
+ case 11:
+ return REJECTED_BY_USER;
+ default:
+ return UNKNOWN;
}
}
}
@@ -311,44 +314,45 @@
/**
* Handles client connections
*/
- private class ClientHandler extends Handler {
+ private class ClientHandler extends WifiHandler {
- ClientHandler(android.os.Looper looper) {
- super(looper);
+ ClientHandler(String tag, android.os.Looper looper) {
+ super(tag, looper);
}
@Override
public void handleMessage(Message msg) {
+ super.handleMessage(msg);
switch (msg.what) {
- case WifiP2pManager.SET_DEVICE_NAME:
- case WifiP2pManager.SET_WFD_INFO:
- case WifiP2pManager.DISCOVER_PEERS:
- case WifiP2pManager.STOP_DISCOVERY:
- case WifiP2pManager.CONNECT:
- case WifiP2pManager.CANCEL_CONNECT:
- case WifiP2pManager.CREATE_GROUP:
- case WifiP2pManager.REMOVE_GROUP:
- case WifiP2pManager.START_LISTEN:
- case WifiP2pManager.STOP_LISTEN:
- case WifiP2pManager.SET_CHANNEL:
- case WifiP2pManager.START_WPS:
- case WifiP2pManager.ADD_LOCAL_SERVICE:
- case WifiP2pManager.REMOVE_LOCAL_SERVICE:
- case WifiP2pManager.CLEAR_LOCAL_SERVICES:
- case WifiP2pManager.DISCOVER_SERVICES:
- case WifiP2pManager.ADD_SERVICE_REQUEST:
- case WifiP2pManager.REMOVE_SERVICE_REQUEST:
- case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
- case WifiP2pManager.REQUEST_PEERS:
- case WifiP2pManager.REQUEST_CONNECTION_INFO:
- case WifiP2pManager.REQUEST_GROUP_INFO:
- case WifiP2pManager.DELETE_PERSISTENT_GROUP:
- case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
- mP2pStateMachine.sendMessage(Message.obtain(msg));
- break;
- default:
- Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg);
- break;
+ case WifiP2pManager.SET_DEVICE_NAME:
+ case WifiP2pManager.SET_WFD_INFO:
+ case WifiP2pManager.DISCOVER_PEERS:
+ case WifiP2pManager.STOP_DISCOVERY:
+ case WifiP2pManager.CONNECT:
+ case WifiP2pManager.CANCEL_CONNECT:
+ case WifiP2pManager.CREATE_GROUP:
+ case WifiP2pManager.REMOVE_GROUP:
+ case WifiP2pManager.START_LISTEN:
+ case WifiP2pManager.STOP_LISTEN:
+ case WifiP2pManager.SET_CHANNEL:
+ case WifiP2pManager.START_WPS:
+ case WifiP2pManager.ADD_LOCAL_SERVICE:
+ case WifiP2pManager.REMOVE_LOCAL_SERVICE:
+ case WifiP2pManager.CLEAR_LOCAL_SERVICES:
+ case WifiP2pManager.DISCOVER_SERVICES:
+ case WifiP2pManager.ADD_SERVICE_REQUEST:
+ case WifiP2pManager.REMOVE_SERVICE_REQUEST:
+ case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
+ case WifiP2pManager.REQUEST_PEERS:
+ case WifiP2pManager.REQUEST_CONNECTION_INFO:
+ case WifiP2pManager.REQUEST_GROUP_INFO:
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
+ mP2pStateMachine.sendMessage(Message.obtain(msg));
+ break;
+ default:
+ Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg);
+ break;
}
}
}
@@ -367,12 +371,14 @@
HandlerThread wifiP2pThread = new HandlerThread("WifiP2pService");
wifiP2pThread.start();
- mClientHandler = new ClientHandler(wifiP2pThread.getLooper());
-
+ mClientHandler = new ClientHandler(TAG, wifiP2pThread.getLooper());
mP2pStateMachine = new P2pStateMachine(TAG, wifiP2pThread.getLooper(), mP2pSupported);
mP2pStateMachine.start();
}
+ /**
+ * Obtains the service interface for Managements services
+ */
public void connectivityServiceReady() {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNwService = INetworkManagementService.Stub.asInterface(b);
@@ -461,6 +467,7 @@
* Get a reference to handler. This is used by a client to establish
* an AsyncChannel communication with WifiP2pService
*/
+ @Override
public Messenger getMessenger() {
enforceAccessPermission();
enforceChangePermission();
@@ -472,6 +479,7 @@
* an AsyncChannel communication with P2pStateMachine
* @hide
*/
+ @Override
public Messenger getP2pStateMachineMessenger() {
enforceConnectivityInternalOrLocationHardwarePermission();
enforceAccessPermission();
@@ -487,13 +495,34 @@
*
* As an example, the driver could reduce the channel dwell time during scanning
* when acting as a source or sink to minimize impact on miracast.
+ * @param int mode of operation
*/
+ @Override
public void setMiracastMode(int mode) {
enforceConnectivityInternalPermission();
+ checkConfigureWifiDisplayPermission();
mP2pStateMachine.sendMessage(SET_MIRACAST_MODE, mode);
}
@Override
+ public void checkConfigureWifiDisplayPermission() {
+ if (!getWfdPermission(Binder.getCallingUid())) {
+ throw new SecurityException("Wifi Display Permission denied for uid = "
+ + Binder.getCallingUid());
+ }
+ }
+
+ private boolean getWfdPermission(int uid) {
+ if (mWifiInjector == null) {
+ mWifiInjector = WifiInjector.getInstance();
+ }
+ WifiPermissionsWrapper wifiPermissionsWrapper = mWifiInjector.getWifiPermissionsWrapper();
+ return wifiPermissionsWrapper.getUidPermission(
+ android.Manifest.permission.CONFIGURE_WIFI_DISPLAY, uid)
+ != PackageManager.PERMISSION_DENIED;
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -533,10 +562,10 @@
// Inactive is when p2p is enabled with no connectivity
private InactiveState mInactiveState = new InactiveState();
private GroupCreatingState mGroupCreatingState = new GroupCreatingState();
- private UserAuthorizingInviteRequestState mUserAuthorizingInviteRequestState
- = new UserAuthorizingInviteRequestState();
- private UserAuthorizingNegotiationRequestState mUserAuthorizingNegotiationRequestState
- = new UserAuthorizingNegotiationRequestState();
+ private UserAuthorizingInviteRequestState mUserAuthorizingInviteRequestState =
+ new UserAuthorizingInviteRequestState();
+ private UserAuthorizingNegotiationRequestState mUserAuthorizingNegotiationRequestState =
+ new UserAuthorizingNegotiationRequestState();
private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState();
private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
private FrequencyConflictState mFrequencyConflictState = new FrequencyConflictState();
@@ -545,28 +574,28 @@
private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState();
private OngoingGroupRemovalState mOngoingGroupRemovalState = new OngoingGroupRemovalState();
- private WifiNative mWifiNative = WifiNative.getP2pNativeInterface();
- private WifiMonitor mWifiMonitor = WifiMonitor.getInstance();
+ private WifiP2pNative mWifiNative = WifiInjector.getInstance().getWifiP2pNative();
+ private WifiP2pMonitor mWifiMonitor = WifiInjector.getInstance().getWifiP2pMonitor();
private final WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
- /* During a connection, supplicant can tell us that a device was lost. From a supplicant's
- * perspective, the discovery stops during connection and it purges device since it does
- * not get latest updates about the device without being in discovery state.
- *
- * From the framework perspective, the device is still there since we are connecting or
- * connected to it. so we keep these devices in a separate list, so that they are removed
- * when connection is cancelled or lost
- */
+ // WifiInjector is lazy initialized in P2p Service
+ private WifiInjector mWifiInjector;
+ // During a connection, supplicant can tell us that a device was lost. From a supplicant's
+ // perspective, the discovery stops during connection and it purges device since it does
+ // not get latest updates about the device without being in discovery state.
+ // From the framework perspective, the device is still there since we are connecting or
+ // connected to it. so we keep these devices in a separate list, so that they are removed
+ // when connection is cancelled or lost
private final WifiP2pDeviceList mPeersLostDuringConnection = new WifiP2pDeviceList();
private final WifiP2pGroupList mGroups = new WifiP2pGroupList(null,
new GroupDeleteListener() {
- @Override
- public void onDeleteGroup(int netId) {
- if (DBG) logd("called onDeleteGroup() netId=" + netId);
- mWifiNative.removeNetwork(netId);
- mWifiNative.saveConfig();
- sendP2pPersistentGroupsChangedBroadcast();
- }
- });
+ @Override
+ public void onDeleteGroup(int netId) {
+ if (DBG) logd("called onDeleteGroup() netId=" + netId);
+ mWifiNative.removeP2pNetwork(netId);
+ mWifiNative.saveConfig();
+ sendP2pPersistentGroupsChangedBroadcast();
+ }
+ });
private final WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
private WifiP2pGroup mGroup;
@@ -578,6 +607,7 @@
P2pStateMachine(String name, Looper looper, boolean p2pSupported) {
super(name, looper);
+ // CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mP2pNotSupportedState, mDefaultState);
addState(mP2pDisablingState, mDefaultState);
@@ -594,6 +624,7 @@
addState(mGroupCreatedState, mP2pEnabledState);
addState(mUserAuthorizingJoinState, mGroupCreatedState);
addState(mOngoingGroupRemovalState, mGroupCreatedState);
+ // CHECKSTYLE:ON IndentationCheck
if (p2pSupported) {
setInitialState(mP2pDisabledState);
@@ -602,2611 +633,2730 @@
}
setLogRecSize(50);
setLogOnlyTransitions(true);
-
String interfaceName = mWifiNative.getInterfaceName();
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.AP_STA_CONNECTED_EVENT, getHandler());
+ WifiP2pMonitor.AP_STA_CONNECTED_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.AP_STA_DISCONNECTED_EVENT, getHandler());
+ WifiP2pMonitor.AP_STA_DISCONNECTED_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.AUTHENTICATION_FAILURE_EVENT, getHandler());
+ WifiP2pMonitor.P2P_DEVICE_FOUND_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.NETWORK_CONNECTION_EVENT, getHandler());
+ WifiP2pMonitor.P2P_DEVICE_LOST_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.NETWORK_DISCONNECTION_EVENT, getHandler());
+ WifiP2pMonitor.P2P_FIND_STOPPED_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_DEVICE_FOUND_EVENT, getHandler());
+ WifiP2pMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_DEVICE_LOST_EVENT, getHandler());
+ WifiP2pMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_FIND_STOPPED_EVENT, getHandler());
+ WifiP2pMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT, getHandler());
+ WifiP2pMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT, getHandler());
+ WifiP2pMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT, getHandler());
+ WifiP2pMonitor.P2P_GROUP_REMOVED_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT, getHandler());
+ WifiP2pMonitor.P2P_GROUP_STARTED_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT, getHandler());
+ WifiP2pMonitor.P2P_INVITATION_RECEIVED_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_GROUP_REMOVED_EVENT, getHandler());
+ WifiP2pMonitor.P2P_INVITATION_RESULT_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_GROUP_STARTED_EVENT, getHandler());
+ WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_INVITATION_RECEIVED_EVENT, getHandler());
+ WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_INVITATION_RESULT_EVENT, getHandler());
+ WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT, getHandler());
+ WifiP2pMonitor.P2P_PROV_DISC_PBC_RSP_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT, getHandler());
+ WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT, getHandler());
+ WifiP2pMonitor.P2P_SERV_DISC_RESP_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT, getHandler());
+ WifiP2pMonitor.SUP_CONNECTION_EVENT, getHandler());
mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT, getHandler());
- mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.P2P_SERV_DISC_RESP_EVENT, getHandler());
- mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.SCAN_RESULTS_EVENT, getHandler());
- mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.SUP_CONNECTION_EVENT, getHandler());
- mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.SUP_DISCONNECTION_EVENT, getHandler());
- mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, getHandler());
- mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.WPS_FAIL_EVENT, getHandler());
- mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.WPS_OVERLAP_EVENT, getHandler());
- mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.WPS_SUCCESS_EVENT, getHandler());
- mWifiMonitor.registerHandler(interfaceName,
- WifiMonitor.WPS_TIMEOUT_EVENT, getHandler());
+ WifiP2pMonitor.SUP_DISCONNECTION_EVENT, getHandler());
}
- class DefaultState extends State {
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- if (DBG) logd("Full connection with WifiStateMachine established");
- mWifiChannel = (AsyncChannel) message.obj;
- } else {
- loge("Full connection failure, error = " + message.arg1);
+ class DefaultState extends State {
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ if (DBG) logd("Full connection with WifiStateMachine established");
+ mWifiChannel = (AsyncChannel) message.obj;
+ } else {
+ loge("Full connection failure, error = " + message.arg1);
+ mWifiChannel = null;
+ transitionTo(mP2pDisabledState);
+ }
+ break;
+
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
+ loge("Send failed, client connection lost");
+ } else {
+ loge("Client connection lost with reason: " + message.arg1);
+ }
mWifiChannel = null;
- }
- break;
-
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
- if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
- loge("Send failed, client connection lost");
- } else {
- loge("Client connection lost with reason: " + message.arg1);
- }
- mWifiChannel = null;
- break;
-
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
- AsyncChannel ac = new AsyncChannel();
- ac.connect(mContext, getHandler(), message.replyTo);
- break;
- case BLOCK_DISCOVERY:
- mDiscoveryBlocked = (message.arg1 == ENABLED ? true : false);
- // always reset this - we went to a state that doesn't support discovery so
- // it would have stopped regardless
- mDiscoveryPostponed = false;
- if (mDiscoveryBlocked) {
- try {
- StateMachine m = (StateMachine)message.obj;
- m.sendMessage(message.arg2);
- } catch (Exception e) {
- loge("unable to send BLOCK_DISCOVERY response: " + e);
- }
- }
- break;
- case WifiP2pManager.DISCOVER_PEERS:
- replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.STOP_DISCOVERY:
- replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.DISCOVER_SERVICES:
- replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.CONNECT:
- replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.CANCEL_CONNECT:
- replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.CREATE_GROUP:
- replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.REMOVE_GROUP:
- replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.ADD_LOCAL_SERVICE:
- replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.REMOVE_LOCAL_SERVICE:
- replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.CLEAR_LOCAL_SERVICES:
- replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.ADD_SERVICE_REQUEST:
- replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.REMOVE_SERVICE_REQUEST:
- replyToMessage(message,
- WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
- replyToMessage(message,
- WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.SET_DEVICE_NAME:
- replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.DELETE_PERSISTENT_GROUP:
- replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.SET_WFD_INFO:
- replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.REQUEST_PEERS:
- replyToMessage(message, WifiP2pManager.RESPONSE_PEERS,
- new WifiP2pDeviceList(mPeers));
- break;
- case WifiP2pManager.REQUEST_CONNECTION_INFO:
- replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO,
- new WifiP2pInfo(mWifiP2pInfo));
- break;
- case WifiP2pManager.REQUEST_GROUP_INFO:
- replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO,
- mGroup != null ? new WifiP2pGroup(mGroup) : null);
- break;
- case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
- replyToMessage(message, WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO,
- new WifiP2pGroupList(mGroups, null));
- break;
- case WifiP2pManager.START_WPS:
- replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.GET_HANDOVER_REQUEST:
- case WifiP2pManager.GET_HANDOVER_SELECT:
- replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE, null);
- break;
- case WifiP2pManager.INITIATOR_REPORT_NFC_HANDOVER:
- case WifiP2pManager.RESPONDER_REPORT_NFC_HANDOVER:
- replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED,
- WifiP2pManager.BUSY);
- break;
- // Ignore
- case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
- case WifiMonitor.SCAN_RESULTS_EVENT:
- case WifiMonitor.SUP_CONNECTION_EVENT:
- case WifiMonitor.SUP_DISCONNECTION_EVENT:
- case WifiMonitor.NETWORK_CONNECTION_EVENT:
- case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
- case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
- case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
- case WifiMonitor.WPS_SUCCESS_EVENT:
- case WifiMonitor.WPS_FAIL_EVENT:
- case WifiMonitor.WPS_OVERLAP_EVENT:
- case WifiMonitor.WPS_TIMEOUT_EVENT:
- case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
- case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
- case WifiMonitor.P2P_DEVICE_LOST_EVENT:
- case WifiMonitor.P2P_FIND_STOPPED_EVENT:
- case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
- case PEER_CONNECTION_USER_ACCEPT:
- case PEER_CONNECTION_USER_REJECT:
- case DISCONNECT_WIFI_RESPONSE:
- case DROP_WIFI_USER_ACCEPT:
- case DROP_WIFI_USER_REJECT:
- case GROUP_CREATING_TIMED_OUT:
- case DISABLE_P2P_TIMED_OUT:
- case IPM_PRE_DHCP_ACTION:
- case IPM_POST_DHCP_ACTION:
- case IPM_DHCP_RESULTS:
- case IPM_PROVISIONING_SUCCESS:
- case IPM_PROVISIONING_FAILURE:
- case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
- case SET_MIRACAST_MODE:
- case WifiP2pManager.START_LISTEN:
- case WifiP2pManager.STOP_LISTEN:
- case WifiP2pManager.SET_CHANNEL:
- case WifiStateMachine.CMD_ENABLE_P2P:
- // Enable is lazy and has no response
- break;
- case WifiStateMachine.CMD_DISABLE_P2P_REQ:
- // If we end up handling in default, p2p is not enabled
- mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
- break;
- /* unexpected group created, remove */
- case WifiMonitor.P2P_GROUP_STARTED_EVENT:
- mGroup = (WifiP2pGroup) message.obj;
- loge("Unexpected group creation, remove " + mGroup);
- mWifiNative.p2pGroupRemove(mGroup.getInterface());
- break;
- // A group formation failure is always followed by
- // a group removed event. Flushing things at group formation
- // failure causes supplicant issues. Ignore right now.
- case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
- break;
- default:
- loge("Unhandled message " + message);
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class P2pNotSupportedState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case WifiP2pManager.DISCOVER_PEERS:
- replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.STOP_DISCOVERY:
- replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.DISCOVER_SERVICES:
- replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.CONNECT:
- replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.CANCEL_CONNECT:
- replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.CREATE_GROUP:
- replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.REMOVE_GROUP:
- replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.ADD_LOCAL_SERVICE:
- replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.REMOVE_LOCAL_SERVICE:
- replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.CLEAR_LOCAL_SERVICES:
- replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.ADD_SERVICE_REQUEST:
- replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.REMOVE_SERVICE_REQUEST:
- replyToMessage(message,
- WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
- replyToMessage(message,
- WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.SET_DEVICE_NAME:
- replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.DELETE_PERSISTENT_GROUP:
- replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.SET_WFD_INFO:
- replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.START_WPS:
- replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.START_LISTEN:
- replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
- case WifiP2pManager.STOP_LISTEN:
- replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED,
- WifiP2pManager.P2P_UNSUPPORTED);
- break;
-
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class P2pDisablingState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- sendMessageDelayed(obtainMessage(DISABLE_P2P_TIMED_OUT,
- ++mDisableP2pTimeoutIndex, 0), DISABLE_P2P_WAIT_TIME_MS);
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiMonitor.SUP_DISCONNECTION_EVENT:
- if (DBG) logd("p2p socket connection lost");
- transitionTo(mP2pDisabledState);
- break;
- case WifiStateMachine.CMD_ENABLE_P2P:
- case WifiStateMachine.CMD_DISABLE_P2P_REQ:
- deferMessage(message);
- break;
- case DISABLE_P2P_TIMED_OUT:
- if (mDisableP2pTimeoutIndex == message.arg1) {
- loge("P2p disable timed out");
transitionTo(mP2pDisabledState);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
+ break;
- @Override
- public void exit() {
- mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
- }
- }
-
- class P2pDisabledState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiStateMachine.CMD_ENABLE_P2P:
- try {
- mNwService.setInterfaceUp(mWifiNative.getInterfaceName());
- } catch (RemoteException re) {
- loge("Unable to change interface settings: " + re);
- } catch (IllegalStateException ie) {
- loge("Unable to change interface settings: " + ie);
- }
- mWifiMonitor.startMonitoring(mWifiNative.getInterfaceName());
- transitionTo(mP2pEnablingState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class P2pEnablingState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiMonitor.SUP_CONNECTION_EVENT:
- if (DBG) logd("P2p socket connection successful");
- transitionTo(mInactiveState);
- break;
- case WifiMonitor.SUP_DISCONNECTION_EVENT:
- loge("P2p socket connection failed");
- transitionTo(mP2pDisabledState);
- break;
- case WifiStateMachine.CMD_ENABLE_P2P:
- case WifiStateMachine.CMD_DISABLE_P2P_REQ:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class P2pEnabledState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- sendP2pStateChangedBroadcast(true);
- mNetworkInfo.setIsAvailable(true);
- sendP2pConnectionChangedBroadcast();
- initializeP2pSettings();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiMonitor.SUP_DISCONNECTION_EVENT:
- loge("Unexpected loss of p2p socket connection");
- transitionTo(mP2pDisabledState);
- break;
- case WifiStateMachine.CMD_ENABLE_P2P:
- //Nothing to do
- break;
- case WifiStateMachine.CMD_DISABLE_P2P_REQ:
- if (mPeers.clear()) {
- sendPeersChangedBroadcast();
- }
- if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
-
- mWifiMonitor.stopMonitoring(mWifiNative.getInterfaceName());
- transitionTo(mP2pDisablingState);
- break;
- case WifiP2pManager.SET_DEVICE_NAME:
- {
- WifiP2pDevice d = (WifiP2pDevice) message.obj;
- if (d != null && setAndPersistDeviceName(d.deviceName)) {
- if (DBG) logd("set device name " + d.deviceName);
- replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
- WifiP2pManager.ERROR);
- }
- break;
- }
- case WifiP2pManager.SET_WFD_INFO:
- {
- WifiP2pWfdInfo d = (WifiP2pWfdInfo) message.obj;
- if (d != null && setWfdInfo(d)) {
- replyToMessage(message, WifiP2pManager.SET_WFD_INFO_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
- WifiP2pManager.ERROR);
- }
- break;
- }
- case BLOCK_DISCOVERY:
- boolean blocked = (message.arg1 == ENABLED ? true : false);
- if (mDiscoveryBlocked == blocked) break;
- mDiscoveryBlocked = blocked;
- if (blocked && mDiscoveryStarted) {
- mWifiNative.p2pStopFind();
- mDiscoveryPostponed = true;
- }
- if (!blocked && mDiscoveryPostponed) {
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
+ AsyncChannel ac = new WifiAsyncChannel(TAG);
+ ac.connect(mContext, getHandler(), message.replyTo);
+ break;
+ case BLOCK_DISCOVERY:
+ mDiscoveryBlocked = (message.arg1 == ENABLED ? true : false);
+ // always reset this - we went to a state that doesn't support discovery so
+ // it would have stopped regardless
mDiscoveryPostponed = false;
- mWifiNative.p2pFind(DISCOVER_TIMEOUT_S);
- }
- if (blocked) {
- try {
- StateMachine m = (StateMachine)message.obj;
- m.sendMessage(message.arg2);
- } catch (Exception e) {
- loge("unable to send BLOCK_DISCOVERY response: " + e);
+ if (mDiscoveryBlocked) {
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ StateMachine m = (StateMachine) message.obj;
+ try {
+ m.sendMessage(message.arg2);
+ } catch (Exception e) {
+ loge("unable to send BLOCK_DISCOVERY response: " + e);
+ }
}
- }
- break;
- case WifiP2pManager.DISCOVER_PEERS:
- if (mDiscoveryBlocked) {
+ break;
+ case WifiP2pManager.DISCOVER_PEERS:
replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
WifiP2pManager.BUSY);
break;
- }
- // do not send service discovery request while normal find operation.
- clearSupplicantServiceRequest();
- if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
- replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
- sendP2pDiscoveryChangedBroadcast(true);
- } else {
- replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
- WifiP2pManager.ERROR);
- }
- break;
- case WifiMonitor.P2P_FIND_STOPPED_EVENT:
- sendP2pDiscoveryChangedBroadcast(false);
- break;
- case WifiP2pManager.STOP_DISCOVERY:
- if (mWifiNative.p2pStopFind()) {
- replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
- } else {
+ case WifiP2pManager.STOP_DISCOVERY:
replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
- WifiP2pManager.ERROR);
- }
- break;
- case WifiP2pManager.DISCOVER_SERVICES:
- if (mDiscoveryBlocked) {
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.DISCOVER_SERVICES:
replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
WifiP2pManager.BUSY);
break;
- }
- if (DBG) logd(getName() + " discover services");
- if (!updateSupplicantServiceRequest()) {
- replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
- WifiP2pManager.NO_SERVICE_REQUESTS);
+ case WifiP2pManager.CONNECT:
+ replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
+ WifiP2pManager.BUSY);
break;
- }
- if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
- replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
- WifiP2pManager.ERROR);
- }
- break;
- case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
- WifiP2pDevice device = (WifiP2pDevice) message.obj;
- if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
- mPeers.updateSupplicantDetails(device);
- sendPeersChangedBroadcast();
- break;
- case WifiMonitor.P2P_DEVICE_LOST_EVENT:
- device = (WifiP2pDevice) message.obj;
- // Gets current details for the one removed
- device = mPeers.remove(device.deviceAddress);
- if (device != null) {
- sendPeersChangedBroadcast();
- }
- break;
- case WifiP2pManager.ADD_LOCAL_SERVICE:
- if (DBG) logd(getName() + " add service");
- WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo)message.obj;
- if (addLocalService(message.replyTo, servInfo)) {
- replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED);
- }
- break;
- case WifiP2pManager.REMOVE_LOCAL_SERVICE:
- if (DBG) logd(getName() + " remove service");
- servInfo = (WifiP2pServiceInfo)message.obj;
- removeLocalService(message.replyTo, servInfo);
- replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_SUCCEEDED);
- break;
- case WifiP2pManager.CLEAR_LOCAL_SERVICES:
- if (DBG) logd(getName() + " clear service");
- clearLocalServices(message.replyTo);
- replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_SUCCEEDED);
- break;
- case WifiP2pManager.ADD_SERVICE_REQUEST:
- if (DBG) logd(getName() + " add service request");
- if (!addServiceRequest(message.replyTo, (WifiP2pServiceRequest)message.obj)) {
- replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED);
+ case WifiP2pManager.CANCEL_CONNECT:
+ replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
+ WifiP2pManager.BUSY);
break;
- }
- replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_SUCCEEDED);
- break;
- case WifiP2pManager.REMOVE_SERVICE_REQUEST:
- if (DBG) logd(getName() + " remove service request");
- removeServiceRequest(message.replyTo, (WifiP2pServiceRequest)message.obj);
- replyToMessage(message, WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED);
- break;
- case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
- if (DBG) logd(getName() + " clear service request");
- clearServiceRequests(message.replyTo);
- replyToMessage(message, WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED);
- break;
- case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
- if (DBG) logd(getName() + " receive service response");
- List<WifiP2pServiceResponse> sdRespList =
- (List<WifiP2pServiceResponse>) message.obj;
- for (WifiP2pServiceResponse resp : sdRespList) {
- WifiP2pDevice dev =
- mPeers.get(resp.getSrcDevice().deviceAddress);
- resp.setSrcDevice(dev);
- sendServiceResponse(resp);
- }
- break;
- case WifiP2pManager.DELETE_PERSISTENT_GROUP:
- if (DBG) logd(getName() + " delete persistent group");
- mGroups.remove(message.arg1);
- replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED);
- break;
- case SET_MIRACAST_MODE:
- mWifiNative.setMiracastMode(message.arg1);
- break;
- case WifiP2pManager.START_LISTEN:
- if (DBG) logd(getName() + " start listen mode");
- mWifiNative.p2pFlush();
- if (mWifiNative.p2pExtListen(true, 500, 500)) {
- replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
- }
- break;
- case WifiP2pManager.STOP_LISTEN:
- if (DBG) logd(getName() + " stop listen mode");
- if (mWifiNative.p2pExtListen(false, 0, 0)) {
- replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
- }
- mWifiNative.p2pFlush();
- break;
- case WifiP2pManager.SET_CHANNEL:
- Bundle p2pChannels = (Bundle) message.obj;
- int lc = p2pChannels.getInt("lc", 0);
- int oc = p2pChannels.getInt("oc", 0);
- if (DBG) logd(getName() + " set listen and operating channel");
- if (mWifiNative.p2pSetChannel(lc, oc)) {
- replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
- }
- break;
- case WifiP2pManager.GET_HANDOVER_REQUEST:
- Bundle requestBundle = new Bundle();
- requestBundle.putString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE,
- mWifiNative.getNfcHandoverRequest());
- replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE,
- requestBundle);
- break;
- case WifiP2pManager.GET_HANDOVER_SELECT:
- Bundle selectBundle = new Bundle();
- selectBundle.putString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE,
- mWifiNative.getNfcHandoverSelect());
- replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE,
- selectBundle);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
-
- @Override
- public void exit() {
- sendP2pDiscoveryChangedBroadcast(false);
- sendP2pStateChangedBroadcast(false);
- mNetworkInfo.setIsAvailable(false);
- }
- }
-
- class InactiveState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- mSavedPeerConfig.invalidate();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiP2pManager.CONNECT:
- if (DBG) logd(getName() + " sending connect");
- WifiP2pConfig config = (WifiP2pConfig) message.obj;
- if (isConfigInvalid(config)) {
- loge("Dropping connect requeset " + config);
- replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
- break;
- }
-
- mAutonomousGroup = false;
- mWifiNative.p2pStopFind();
- if (reinvokePersistentGroup(config)) {
- transitionTo(mGroupNegotiationState);
- } else {
- transitionTo(mProvisionDiscoveryState);
- }
- mSavedPeerConfig = config;
- mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
- sendPeersChangedBroadcast();
- replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
- break;
- case WifiP2pManager.STOP_DISCOVERY:
- if (mWifiNative.p2pStopFind()) {
- // When discovery stops in inactive state, flush to clear
- // state peer data
- mWifiNative.p2pFlush();
- mServiceDiscReqId = null;
- replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
- WifiP2pManager.ERROR);
- }
- break;
- case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
- config = (WifiP2pConfig) message.obj;
- if (isConfigInvalid(config)) {
- loge("Dropping GO neg request " + config);
- break;
- }
- mSavedPeerConfig = config;
- mAutonomousGroup = false;
- mJoinExistingGroup = false;
- transitionTo(mUserAuthorizingNegotiationRequestState);
- break;
- case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
- WifiP2pGroup group = (WifiP2pGroup) message.obj;
- WifiP2pDevice owner = group.getOwner();
-
- if (owner == null) {
- int id = group.getNetworkId();
- if (id < 0) {
- loge("Ignored invitation from null owner");
- break;
- }
-
- String addr = mGroups.getOwnerAddr(id);
- if (addr != null) {
- group.setOwner(new WifiP2pDevice(addr));
- owner = group.getOwner();
- } else {
- loge("Ignored invitation from null owner");
- break;
- }
- }
-
- config = new WifiP2pConfig();
- config.deviceAddress = group.getOwner().deviceAddress;
-
- if (isConfigInvalid(config)) {
- loge("Dropping invitation request " + config);
- break;
- }
- mSavedPeerConfig = config;
-
- //Check if we have the owner in peer list and use appropriate
- //wps method. Default is to use PBC.
- if ((owner = mPeers.get(owner.deviceAddress)) != null) {
- if (owner.wpsPbcSupported()) {
- mSavedPeerConfig.wps.setup = WpsInfo.PBC;
- } else if (owner.wpsKeypadSupported()) {
- mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
- } else if (owner.wpsDisplaySupported()) {
- mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
- }
- }
-
- mAutonomousGroup = false;
- mJoinExistingGroup = true;
- transitionTo(mUserAuthorizingInviteRequestState);
- break;
- case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
- case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
- case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
- //We let the supplicant handle the provision discovery response
- //and wait instead for the GO_NEGOTIATION_REQUEST_EVENT.
- //Handling provision discovery and issuing a p2p_connect before
- //group negotiation comes through causes issues
- break;
- case WifiP2pManager.CREATE_GROUP:
- mAutonomousGroup = true;
- int netId = message.arg1;
- boolean ret = false;
- if (netId == WifiP2pGroup.PERSISTENT_NET_ID) {
- // check if the go persistent group is present.
- netId = mGroups.getNetworkId(mThisDevice.deviceAddress);
- if (netId != -1) {
- ret = mWifiNative.p2pGroupAdd(netId);
- } else {
- ret = mWifiNative.p2pGroupAdd(true);
- }
- } else {
- ret = mWifiNative.p2pGroupAdd(false);
- }
-
- if (ret) {
- replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
- transitionTo(mGroupNegotiationState);
- } else {
+ case WifiP2pManager.CREATE_GROUP:
replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
- WifiP2pManager.ERROR);
- // remain at this state.
- }
- break;
- case WifiMonitor.P2P_GROUP_STARTED_EVENT:
- mGroup = (WifiP2pGroup) message.obj;
- if (DBG) logd(getName() + " group started");
-
- // We hit this scenario when a persistent group is reinvoked
- if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
- mAutonomousGroup = false;
- deferMessage(message);
- transitionTo(mGroupNegotiationState);
- } else {
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.REMOVE_GROUP:
+ replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.ADD_LOCAL_SERVICE:
+ replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.REMOVE_LOCAL_SERVICE:
+ replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.CLEAR_LOCAL_SERVICES:
+ replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.ADD_SERVICE_REQUEST:
+ replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.REMOVE_SERVICE_REQUEST:
+ replyToMessage(message,
+ WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
+ replyToMessage(message,
+ WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.SET_DEVICE_NAME:
+ replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.SET_WFD_INFO:
+ if (!getWfdPermission(message.sendingUid)) {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.ERROR);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.BUSY);
+ }
+ break;
+ case WifiP2pManager.REQUEST_PEERS:
+ replyToMessage(message, WifiP2pManager.RESPONSE_PEERS,
+ getPeers((Bundle) message.obj, message.sendingUid));
+ break;
+ case WifiP2pManager.REQUEST_CONNECTION_INFO:
+ replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO,
+ new WifiP2pInfo(mWifiP2pInfo));
+ break;
+ case WifiP2pManager.REQUEST_GROUP_INFO:
+ replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO,
+ mGroup != null ? new WifiP2pGroup(mGroup) : null);
+ break;
+ case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO:
+ replyToMessage(message, WifiP2pManager.RESPONSE_PERSISTENT_GROUP_INFO,
+ new WifiP2pGroupList(mGroups, null));
+ break;
+ case WifiP2pManager.START_WPS:
+ replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.GET_HANDOVER_REQUEST:
+ case WifiP2pManager.GET_HANDOVER_SELECT:
+ replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE, null);
+ break;
+ case WifiP2pManager.INITIATOR_REPORT_NFC_HANDOVER:
+ case WifiP2pManager.RESPONDER_REPORT_NFC_HANDOVER:
+ replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pMonitor.P2P_INVITATION_RESULT_EVENT:
+ case WifiP2pMonitor.SUP_CONNECTION_EVENT:
+ case WifiP2pMonitor.SUP_DISCONNECTION_EVENT:
+ case WifiP2pMonitor.P2P_GROUP_REMOVED_EVENT:
+ case WifiP2pMonitor.P2P_DEVICE_FOUND_EVENT:
+ case WifiP2pMonitor.P2P_DEVICE_LOST_EVENT:
+ case WifiP2pMonitor.P2P_FIND_STOPPED_EVENT:
+ case WifiP2pMonitor.P2P_SERV_DISC_RESP_EVENT:
+ case PEER_CONNECTION_USER_ACCEPT:
+ case PEER_CONNECTION_USER_REJECT:
+ case DISCONNECT_WIFI_RESPONSE:
+ case DROP_WIFI_USER_ACCEPT:
+ case DROP_WIFI_USER_REJECT:
+ case GROUP_CREATING_TIMED_OUT:
+ case DISABLE_P2P_TIMED_OUT:
+ case IPM_PRE_DHCP_ACTION:
+ case IPM_POST_DHCP_ACTION:
+ case IPM_DHCP_RESULTS:
+ case IPM_PROVISIONING_SUCCESS:
+ case IPM_PROVISIONING_FAILURE:
+ case WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT:
+ case SET_MIRACAST_MODE:
+ case WifiP2pManager.START_LISTEN:
+ case WifiP2pManager.STOP_LISTEN:
+ case WifiP2pManager.SET_CHANNEL:
+ case WifiStateMachine.CMD_ENABLE_P2P:
+ // Enable is lazy and has no response
+ break;
+ case WifiStateMachine.CMD_DISABLE_P2P_REQ:
+ // If we end up handling in default, p2p is not enabled
+ if (mWifiChannel != null) {
+ mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
+ } else {
+ loge("Unexpected disable request when WifiChannel is null");
+ }
+ break;
+ case WifiP2pMonitor.P2P_GROUP_STARTED_EVENT:
+ // unexpected group created, remove
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal arguments");
+ break;
+ }
+ mGroup = (WifiP2pGroup) message.obj;
loge("Unexpected group creation, remove " + mGroup);
mWifiNative.p2pGroupRemove(mGroup.getInterface());
- }
- break;
- case WifiP2pManager.START_LISTEN:
- if (DBG) logd(getName() + " start listen mode");
- mWifiNative.p2pFlush();
- if (mWifiNative.p2pExtListen(true, 500, 500)) {
- replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
- }
- break;
- case WifiP2pManager.STOP_LISTEN:
- if (DBG) logd(getName() + " stop listen mode");
- if (mWifiNative.p2pExtListen(false, 0, 0)) {
- replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
- }
- mWifiNative.p2pFlush();
- break;
- case WifiP2pManager.SET_CHANNEL:
- Bundle p2pChannels = (Bundle) message.obj;
- int lc = p2pChannels.getInt("lc", 0);
- int oc = p2pChannels.getInt("oc", 0);
- if (DBG) logd(getName() + " set listen and operating channel");
- if (mWifiNative.p2pSetChannel(lc, oc)) {
- replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
- }
- break;
- case WifiP2pManager.INITIATOR_REPORT_NFC_HANDOVER:
- String handoverSelect = null;
-
- if (message.obj != null) {
- handoverSelect = ((Bundle) message.obj)
- .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE);
- }
-
- if (handoverSelect != null
- && mWifiNative.initiatorReportNfcHandover(handoverSelect)) {
- replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED);
- transitionTo(mGroupCreatingState);
- } else {
- replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED);
- }
- break;
- case WifiP2pManager.RESPONDER_REPORT_NFC_HANDOVER:
- String handoverRequest = null;
-
- if (message.obj != null) {
- handoverRequest = ((Bundle) message.obj)
- .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE);
- }
-
- if (handoverRequest != null
- && mWifiNative.responderReportNfcHandover(handoverRequest)) {
- replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED);
- transitionTo(mGroupCreatingState);
- } else {
- replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED);
- }
- break;
- default:
- return NOT_HANDLED;
+ break;
+ case WifiP2pMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
+ // A group formation failure is always followed by
+ // a group removed event. Flushing things at group formation
+ // failure causes supplicant issues. Ignore right now.
+ break;
+ default:
+ loge("Unhandled message " + message);
+ return NOT_HANDLED;
+ }
+ return HANDLED;
}
- return HANDLED;
- }
- }
-
- class GroupCreatingState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,
- ++mGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);
}
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- boolean ret = HANDLED;
- switch (message.what) {
- case GROUP_CREATING_TIMED_OUT:
- if (mGroupCreatingTimeoutIndex == message.arg1) {
- if (DBG) logd("Group negotiation timed out");
- handleGroupCreationFailure();
- transitionTo(mInactiveState);
- }
- break;
- case WifiMonitor.P2P_DEVICE_LOST_EVENT:
- WifiP2pDevice device = (WifiP2pDevice) message.obj;
- if (!mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
- if (DBG) {
- logd("mSavedPeerConfig " + mSavedPeerConfig.deviceAddress +
- "device " + device.deviceAddress);
+ class P2pNotSupportedState extends State {
+ @Override
+ public boolean processMessage(Message message) {
+ switch (message.what) {
+ case WifiP2pManager.DISCOVER_PEERS:
+ replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.STOP_DISCOVERY:
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.DISCOVER_SERVICES:
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.CONNECT:
+ replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.CANCEL_CONNECT:
+ replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.CREATE_GROUP:
+ replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.REMOVE_GROUP:
+ replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.ADD_LOCAL_SERVICE:
+ replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.REMOVE_LOCAL_SERVICE:
+ replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.CLEAR_LOCAL_SERVICES:
+ replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.ADD_SERVICE_REQUEST:
+ replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.REMOVE_SERVICE_REQUEST:
+ replyToMessage(message,
+ WifiP2pManager.REMOVE_SERVICE_REQUEST_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
+ replyToMessage(message,
+ WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.SET_DEVICE_NAME:
+ replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.SET_WFD_INFO:
+ if (!getWfdPermission(message.sendingUid)) {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.ERROR);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
}
- // Do the regular device lost handling
- ret = NOT_HANDLED;
+ break;
+ case WifiP2pManager.START_WPS:
+ replyToMessage(message, WifiP2pManager.START_WPS_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.START_LISTEN:
+ replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+ case WifiP2pManager.STOP_LISTEN:
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
+
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class P2pDisablingState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ sendMessageDelayed(obtainMessage(DISABLE_P2P_TIMED_OUT,
+ ++sDisableP2pTimeoutIndex, 0), DISABLE_P2P_WAIT_TIME_MS);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case WifiP2pMonitor.SUP_DISCONNECTION_EVENT:
+ if (DBG) logd("p2p socket connection lost");
+ transitionTo(mP2pDisabledState);
+ break;
+ case WifiStateMachine.CMD_ENABLE_P2P:
+ case WifiStateMachine.CMD_DISABLE_P2P_REQ:
+ deferMessage(message);
+ break;
+ case DISABLE_P2P_TIMED_OUT:
+ if (sDisableP2pTimeoutIndex == message.arg1) {
+ loge("P2p disable timed out");
+ transitionTo(mP2pDisabledState);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ if (mWifiChannel != null) {
+ mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
+ } else {
+ loge("P2pDisablingState exit(): WifiChannel is null");
+ }
+ }
+ }
+
+ class P2pDisabledState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case WifiStateMachine.CMD_ENABLE_P2P:
+ try {
+ mNwService.setInterfaceUp(mWifiNative.getInterfaceName());
+ } catch (RemoteException re) {
+ loge("Unable to change interface settings: " + re);
+ } catch (IllegalStateException ie) {
+ loge("Unable to change interface settings: " + ie);
+ }
+ mWifiMonitor.startMonitoring(mWifiNative.getInterfaceName());
+ transitionTo(mP2pEnablingState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class P2pEnablingState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case WifiP2pMonitor.SUP_CONNECTION_EVENT:
+ if (DBG) logd("P2p socket connection successful");
+ transitionTo(mInactiveState);
+ break;
+ case WifiP2pMonitor.SUP_DISCONNECTION_EVENT:
+ loge("P2p socket connection failed");
+ transitionTo(mP2pDisabledState);
+ break;
+ case WifiStateMachine.CMD_ENABLE_P2P:
+ case WifiStateMachine.CMD_DISABLE_P2P_REQ:
+ deferMessage(message);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class P2pEnabledState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ sendP2pStateChangedBroadcast(true);
+ mNetworkInfo.setIsAvailable(true);
+ sendP2pConnectionChangedBroadcast();
+ initializeP2pSettings();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case WifiP2pMonitor.SUP_DISCONNECTION_EVENT:
+ loge("Unexpected loss of p2p socket connection");
+ transitionTo(mP2pDisabledState);
+ break;
+ case WifiStateMachine.CMD_ENABLE_P2P:
+ // Nothing to do
+ break;
+ case WifiStateMachine.CMD_DISABLE_P2P_REQ:
+ if (mPeers.clear()) {
+ sendPeersChangedBroadcast();
+ }
+ if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast();
+
+ mWifiMonitor.stopMonitoring(mWifiNative.getInterfaceName());
+ transitionTo(mP2pDisablingState);
+ break;
+ case WifiP2pManager.SET_DEVICE_NAME:
+ {
+ WifiP2pDevice d = (WifiP2pDevice) message.obj;
+ if (d != null && setAndPersistDeviceName(d.deviceName)) {
+ if (DBG) logd("set device name " + d.deviceName);
+ replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_DEVICE_NAME_FAILED,
+ WifiP2pManager.ERROR);
+ }
break;
}
- // Do nothing
- if (DBG) logd("Add device to lost list " + device);
- mPeersLostDuringConnection.updateSupplicantDetails(device);
- break;
- case WifiP2pManager.DISCOVER_PEERS:
- /* Discovery will break negotiation */
- replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
- WifiP2pManager.BUSY);
- break;
- case WifiP2pManager.CANCEL_CONNECT:
- //Do a supplicant p2p_cancel which only cancels an ongoing
- //group negotiation. This will fail for a pending provision
- //discovery or for a pending user action, but at the framework
- //level, we always treat cancel as succeeded and enter
- //an inactive state
- mWifiNative.p2pCancelConnect();
- handleGroupCreationFailure();
- transitionTo(mInactiveState);
- replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
- break;
- case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
- // We hit this scenario when NFC handover is invoked.
- mAutonomousGroup = false;
- transitionTo(mGroupNegotiationState);
- break;
- default:
- ret = NOT_HANDLED;
- }
- return ret;
- }
- }
-
- class UserAuthorizingNegotiationRequestState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- notifyInvitationReceived();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- boolean ret = HANDLED;
- switch (message.what) {
- case PEER_CONNECTION_USER_ACCEPT:
- mWifiNative.p2pStopFind();
- p2pConnectWithPinDisplay(mSavedPeerConfig);
- mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
- sendPeersChangedBroadcast();
- transitionTo(mGroupNegotiationState);
- break;
- case PEER_CONNECTION_USER_REJECT:
- if (DBG) logd("User rejected negotiation " + mSavedPeerConfig);
- transitionTo(mInactiveState);
- break;
- default:
- return NOT_HANDLED;
- }
- return ret;
- }
-
- @Override
- public void exit() {
- //TODO: dismiss dialog if not already done
- }
- }
-
- class UserAuthorizingInviteRequestState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- notifyInvitationReceived();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- boolean ret = HANDLED;
- switch (message.what) {
- case PEER_CONNECTION_USER_ACCEPT:
- mWifiNative.p2pStopFind();
- if (!reinvokePersistentGroup(mSavedPeerConfig)) {
- // Do negotiation when persistence fails
- p2pConnectWithPinDisplay(mSavedPeerConfig);
+ case WifiP2pManager.SET_WFD_INFO:
+ {
+ WifiP2pWfdInfo d = (WifiP2pWfdInfo) message.obj;
+ if (!getWfdPermission(message.sendingUid)) {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.ERROR);
+ } else if (d != null && setWfdInfo(d)) {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ break;
}
- mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
- sendPeersChangedBroadcast();
- transitionTo(mGroupNegotiationState);
- break;
- case PEER_CONNECTION_USER_REJECT:
- if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
- transitionTo(mInactiveState);
- break;
- default:
- return NOT_HANDLED;
+ case BLOCK_DISCOVERY:
+ boolean blocked = (message.arg1 == ENABLED ? true : false);
+ if (mDiscoveryBlocked == blocked) break;
+ mDiscoveryBlocked = blocked;
+ if (blocked && mDiscoveryStarted) {
+ mWifiNative.p2pStopFind();
+ mDiscoveryPostponed = true;
+ }
+ if (!blocked && mDiscoveryPostponed) {
+ mDiscoveryPostponed = false;
+ mWifiNative.p2pFind(DISCOVER_TIMEOUT_S);
+ }
+ if (blocked) {
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ StateMachine m = (StateMachine) message.obj;
+ try {
+ m.sendMessage(message.arg2);
+ } catch (Exception e) {
+ loge("unable to send BLOCK_DISCOVERY response: " + e);
+ }
+ }
+ break;
+ case WifiP2pManager.DISCOVER_PEERS:
+ if (mDiscoveryBlocked) {
+ replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ }
+ // do not send service discovery request while normal find operation.
+ clearSupplicantServiceRequest();
+ if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
+ replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
+ sendP2pDiscoveryChangedBroadcast(true);
+ } else {
+ replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ break;
+ case WifiP2pMonitor.P2P_FIND_STOPPED_EVENT:
+ sendP2pDiscoveryChangedBroadcast(false);
+ break;
+ case WifiP2pManager.STOP_DISCOVERY:
+ if (mWifiNative.p2pStopFind()) {
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ break;
+ case WifiP2pManager.DISCOVER_SERVICES:
+ if (mDiscoveryBlocked) {
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ }
+ if (DBG) logd(getName() + " discover services");
+ if (!updateSupplicantServiceRequest()) {
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
+ WifiP2pManager.NO_SERVICE_REQUESTS);
+ break;
+ }
+ if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ break;
+ case WifiP2pMonitor.P2P_DEVICE_FOUND_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ WifiP2pDevice device = (WifiP2pDevice) message.obj;
+ if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
+ mPeers.updateSupplicantDetails(device);
+ sendPeersChangedBroadcast();
+ break;
+ case WifiP2pMonitor.P2P_DEVICE_LOST_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ device = (WifiP2pDevice) message.obj;
+ // Gets current details for the one removed
+ device = mPeers.remove(device.deviceAddress);
+ if (device != null) {
+ sendPeersChangedBroadcast();
+ }
+ break;
+ case WifiP2pManager.ADD_LOCAL_SERVICE:
+ if (DBG) logd(getName() + " add service");
+ WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo) message.obj;
+ if (addLocalService(message.replyTo, servInfo)) {
+ replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED);
+ }
+ break;
+ case WifiP2pManager.REMOVE_LOCAL_SERVICE:
+ if (DBG) logd(getName() + " remove service");
+ servInfo = (WifiP2pServiceInfo) message.obj;
+ removeLocalService(message.replyTo, servInfo);
+ replyToMessage(message, WifiP2pManager.REMOVE_LOCAL_SERVICE_SUCCEEDED);
+ break;
+ case WifiP2pManager.CLEAR_LOCAL_SERVICES:
+ if (DBG) logd(getName() + " clear service");
+ clearLocalServices(message.replyTo);
+ replyToMessage(message, WifiP2pManager.CLEAR_LOCAL_SERVICES_SUCCEEDED);
+ break;
+ case WifiP2pManager.ADD_SERVICE_REQUEST:
+ if (DBG) logd(getName() + " add service request");
+ if (!addServiceRequest(message.replyTo,
+ (WifiP2pServiceRequest) message.obj)) {
+ replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED);
+ break;
+ }
+ replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_SUCCEEDED);
+ break;
+ case WifiP2pManager.REMOVE_SERVICE_REQUEST:
+ if (DBG) logd(getName() + " remove service request");
+ removeServiceRequest(message.replyTo, (WifiP2pServiceRequest) message.obj);
+ replyToMessage(message, WifiP2pManager.REMOVE_SERVICE_REQUEST_SUCCEEDED);
+ break;
+ case WifiP2pManager.CLEAR_SERVICE_REQUESTS:
+ if (DBG) logd(getName() + " clear service request");
+ clearServiceRequests(message.replyTo);
+ replyToMessage(message, WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED);
+ break;
+ case WifiP2pMonitor.P2P_SERV_DISC_RESP_EVENT:
+ if (DBG) logd(getName() + " receive service response");
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ List<WifiP2pServiceResponse> sdRespList =
+ (List<WifiP2pServiceResponse>) message.obj;
+ for (WifiP2pServiceResponse resp : sdRespList) {
+ WifiP2pDevice dev =
+ mPeers.get(resp.getSrcDevice().deviceAddress);
+ resp.setSrcDevice(dev);
+ sendServiceResponse(resp);
+ }
+ break;
+ case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+ if (DBG) logd(getName() + " delete persistent group");
+ mGroups.remove(message.arg1);
+ replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED);
+ break;
+ case SET_MIRACAST_MODE:
+ mWifiNative.setMiracastMode(message.arg1);
+ break;
+ case WifiP2pManager.START_LISTEN:
+ if (DBG) logd(getName() + " start listen mode");
+ mWifiNative.p2pFlush();
+ if (mWifiNative.p2pExtListen(true, 500, 500)) {
+ replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
+ }
+ break;
+ case WifiP2pManager.STOP_LISTEN:
+ if (DBG) logd(getName() + " stop listen mode");
+ if (mWifiNative.p2pExtListen(false, 0, 0)) {
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
+ }
+ mWifiNative.p2pFlush();
+ break;
+ case WifiP2pManager.SET_CHANNEL:
+ Bundle p2pChannels = (Bundle) message.obj;
+ int lc = p2pChannels.getInt("lc", 0);
+ int oc = p2pChannels.getInt("oc", 0);
+ if (DBG) logd(getName() + " set listen and operating channel");
+ if (mWifiNative.p2pSetChannel(lc, oc)) {
+ replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
+ }
+ break;
+ case WifiP2pManager.GET_HANDOVER_REQUEST:
+ Bundle requestBundle = new Bundle();
+ requestBundle.putString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE,
+ mWifiNative.getNfcHandoverRequest());
+ replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE,
+ requestBundle);
+ break;
+ case WifiP2pManager.GET_HANDOVER_SELECT:
+ Bundle selectBundle = new Bundle();
+ selectBundle.putString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE,
+ mWifiNative.getNfcHandoverSelect());
+ replyToMessage(message, WifiP2pManager.RESPONSE_GET_HANDOVER_MESSAGE,
+ selectBundle);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
}
- return ret;
+
+ @Override
+ public void exit() {
+ sendP2pDiscoveryChangedBroadcast(false);
+ sendP2pStateChangedBroadcast(false);
+ mNetworkInfo.setIsAvailable(false);
+ }
}
- @Override
- public void exit() {
- //TODO: dismiss dialog if not already done
- }
- }
+ class InactiveState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ mSavedPeerConfig.invalidate();
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case WifiP2pManager.CONNECT:
+ if (DBG) logd(getName() + " sending connect");
+ WifiP2pConfig config = (WifiP2pConfig) message.obj;
+ if (isConfigInvalid(config)) {
+ loge("Dropping connect requeset " + config);
+ replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
+ break;
+ }
-
- class ProvisionDiscoveryState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- WifiP2pProvDiscEvent provDisc;
- WifiP2pDevice device;
- switch (message.what) {
- case WifiMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
- provDisc = (WifiP2pProvDiscEvent) message.obj;
- device = provDisc.device;
- if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
-
- if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
- if (DBG) logd("Found a match " + mSavedPeerConfig);
- p2pConnectWithPinDisplay(mSavedPeerConfig);
- transitionTo(mGroupNegotiationState);
- }
- break;
- case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
- provDisc = (WifiP2pProvDiscEvent) message.obj;
- device = provDisc.device;
- if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
-
- if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) {
- if (DBG) logd("Found a match " + mSavedPeerConfig);
- /* we already have the pin */
- if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
- p2pConnectWithPinDisplay(mSavedPeerConfig);
+ mAutonomousGroup = false;
+ mWifiNative.p2pStopFind();
+ if (reinvokePersistentGroup(config)) {
transitionTo(mGroupNegotiationState);
} else {
- mJoinExistingGroup = false;
- transitionTo(mUserAuthorizingNegotiationRequestState);
+ transitionTo(mProvisionDiscoveryState);
}
- }
- break;
- case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
- provDisc = (WifiP2pProvDiscEvent) message.obj;
- device = provDisc.device;
- if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) break;
-
- if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
- if (DBG) logd("Found a match " + mSavedPeerConfig);
- mSavedPeerConfig.wps.pin = provDisc.pin;
- p2pConnectWithPinDisplay(mSavedPeerConfig);
- notifyInvitationSent(provDisc.pin, device.deviceAddress);
- transitionTo(mGroupNegotiationState);
- }
- break;
- case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
- loge("provision discovery failed");
- handleGroupCreationFailure();
- transitionTo(mInactiveState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class GroupNegotiationState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- // We ignore these right now, since we get a GROUP_STARTED notification
- // afterwards
- case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
- case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
- if (DBG) logd(getName() + " go success");
- break;
- case WifiMonitor.P2P_GROUP_STARTED_EVENT:
- mGroup = (WifiP2pGroup) message.obj;
- if (DBG) logd(getName() + " group started");
-
- if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
- /*
- * update cache information and set network id to mGroup.
- */
- updatePersistentNetworks(NO_RELOAD);
- String devAddr = mGroup.getOwner().deviceAddress;
- mGroup.setNetworkId(mGroups.getNetworkId(devAddr,
- mGroup.getNetworkName()));
- }
-
- if (mGroup.isGroupOwner()) {
- /* Setting an idle time out on GO causes issues with certain scenarios
- * on clients where it can be off-channel for longer and with the power
- * save modes used.
- *
- * TODO: Verify multi-channel scenarios and supplicant behavior are
- * better before adding a time out in future
- */
- //Set group idle timeout of 10 sec, to avoid GO beaconing incase of any
- //failure during 4-way Handshake.
- if (!mAutonomousGroup) {
- mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
- }
- startDhcpServer(mGroup.getInterface());
- } else {
- mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
- startIpManager(mGroup.getInterface());
- WifiP2pDevice groupOwner = mGroup.getOwner();
- WifiP2pDevice peer = mPeers.get(groupOwner.deviceAddress);
- if (peer != null) {
- // update group owner details with peer details found at discovery
- groupOwner.updateSupplicantDetails(peer);
- mPeers.updateStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
- sendPeersChangedBroadcast();
+ mSavedPeerConfig = config;
+ mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
+ sendPeersChangedBroadcast();
+ replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
+ break;
+ case WifiP2pManager.STOP_DISCOVERY:
+ if (mWifiNative.p2pStopFind()) {
+ // When discovery stops in inactive state, flush to clear
+ // state peer data
+ mWifiNative.p2pFlush();
+ mServiceDiscReqId = null;
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
} else {
- // A supplicant bug can lead to reporting an invalid
- // group owner address (all zeroes) at times. Avoid a
- // crash, but continue group creation since it is not
- // essential.
- logw("Unknown group owner " + groupOwner);
+ replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+ WifiP2pManager.ERROR);
}
- }
- transitionTo(mGroupCreatedState);
- break;
- case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
- P2pStatus status = (P2pStatus) message.obj;
- if (status == P2pStatus.NO_COMMON_CHANNEL) {
- transitionTo(mFrequencyConflictState);
break;
- }
- /* continue with group removal handling */
- case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
- if (DBG) logd(getName() + " go failure");
- handleGroupCreationFailure();
- transitionTo(mInactiveState);
- break;
- // A group formation failure is always followed by
- // a group removed event. Flushing things at group formation
- // failure causes supplicant issues. Ignore right now.
- case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
- status = (P2pStatus) message.obj;
- if (status == P2pStatus.NO_COMMON_CHANNEL) {
- transitionTo(mFrequencyConflictState);
+ case WifiP2pMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
+ config = (WifiP2pConfig) message.obj;
+ if (isConfigInvalid(config)) {
+ loge("Dropping GO neg request " + config);
+ break;
+ }
+ mSavedPeerConfig = config;
+ mAutonomousGroup = false;
+ mJoinExistingGroup = false;
+ transitionTo(mUserAuthorizingNegotiationRequestState);
break;
- }
- break;
- case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
- status = (P2pStatus)message.obj;
- if (status == P2pStatus.SUCCESS) {
- // invocation was succeeded.
- // wait P2P_GROUP_STARTED_EVENT.
- break;
- }
- loge("Invitation result " + status);
- if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
- // target device has already removed the credential.
- // So, remove this credential accordingly.
- int netId = mSavedPeerConfig.netId;
- if (netId >= 0) {
- if (DBG) logd("Remove unknown client from the list");
- removeClientFromList(netId, mSavedPeerConfig.deviceAddress, true);
+ case WifiP2pMonitor.P2P_INVITATION_RECEIVED_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Invalid argument(s)");
+ break;
+ }
+ WifiP2pGroup group = (WifiP2pGroup) message.obj;
+ WifiP2pDevice owner = group.getOwner();
+ if (owner == null) {
+ int id = group.getNetworkId();
+ if (id < 0) {
+ loge("Ignored invitation from null owner");
+ break;
+ }
+
+ String addr = mGroups.getOwnerAddr(id);
+ if (addr != null) {
+ group.setOwner(new WifiP2pDevice(addr));
+ owner = group.getOwner();
+ } else {
+ loge("Ignored invitation from null owner");
+ break;
+ }
+ }
+ config = new WifiP2pConfig();
+ config.deviceAddress = group.getOwner().deviceAddress;
+ if (isConfigInvalid(config)) {
+ loge("Dropping invitation request " + config);
+ break;
+ }
+ mSavedPeerConfig = config;
+
+ // Check if we have the owner in peer list and use appropriate
+ // wps method. Default is to use PBC.
+ if (owner != null && ((owner = mPeers.get(owner.deviceAddress)) != null)) {
+ if (owner.wpsPbcSupported()) {
+ mSavedPeerConfig.wps.setup = WpsInfo.PBC;
+ } else if (owner.wpsKeypadSupported()) {
+ mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
+ } else if (owner.wpsDisplaySupported()) {
+ mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
+ }
}
- // Reinvocation has failed, try group negotiation
- mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
- p2pConnectWithPinDisplay(mSavedPeerConfig);
- } else if (status == P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE) {
+ mAutonomousGroup = false;
+ mJoinExistingGroup = true;
+ transitionTo(mUserAuthorizingInviteRequestState);
+ break;
+ case WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
+ case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
+ case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
+ // We let the supplicant handle the provision discovery response
+ // and wait instead for the GO_NEGOTIATION_REQUEST_EVENT.
+ // Handling provision discovery and issuing a p2p_connect before
+ // group negotiation comes through causes issues
+ break;
+ case WifiP2pManager.CREATE_GROUP:
+ mAutonomousGroup = true;
+ int netId = message.arg1;
+ boolean ret = false;
+ if (netId == WifiP2pGroup.PERSISTENT_NET_ID) {
+ // check if the go persistent group is present.
+ netId = mGroups.getNetworkId(mThisDevice.deviceAddress);
+ if (netId != -1) {
+ ret = mWifiNative.p2pGroupAdd(netId);
+ } else {
+ ret = mWifiNative.p2pGroupAdd(true);
+ }
+ } else {
+ ret = mWifiNative.p2pGroupAdd(false);
+ }
- // Devices setting persistent_reconnect to 0 in wpa_supplicant
- // always defer the invocation request and return
- // "information is currently unable" error.
- // So, try another way to connect for interoperability.
- mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
- p2pConnectWithPinDisplay(mSavedPeerConfig);
- } else if (status == P2pStatus.NO_COMMON_CHANNEL) {
- transitionTo(mFrequencyConflictState);
- } else {
+ if (ret) {
+ replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
+ transitionTo(mGroupNegotiationState);
+ } else {
+ replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
+ WifiP2pManager.ERROR);
+ // remain at this state.
+ }
+ break;
+ case WifiP2pMonitor.P2P_GROUP_STARTED_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Invalid argument(s)");
+ break;
+ }
+ mGroup = (WifiP2pGroup) message.obj;
+ if (DBG) logd(getName() + " group started");
+
+ // We hit this scenario when a persistent group is reinvoked
+ if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
+ mAutonomousGroup = false;
+ deferMessage(message);
+ transitionTo(mGroupNegotiationState);
+ } else {
+ loge("Unexpected group creation, remove " + mGroup);
+ mWifiNative.p2pGroupRemove(mGroup.getInterface());
+ }
+ break;
+ case WifiP2pManager.START_LISTEN:
+ if (DBG) logd(getName() + " start listen mode");
+ mWifiNative.p2pFlush();
+ if (mWifiNative.p2pExtListen(true, 500, 500)) {
+ replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
+ }
+ break;
+ case WifiP2pManager.STOP_LISTEN:
+ if (DBG) logd(getName() + " stop listen mode");
+ if (mWifiNative.p2pExtListen(false, 0, 0)) {
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED);
+ }
+ mWifiNative.p2pFlush();
+ break;
+ case WifiP2pManager.SET_CHANNEL:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal arguments(s)");
+ break;
+ }
+ Bundle p2pChannels = (Bundle) message.obj;
+ int lc = p2pChannels.getInt("lc", 0);
+ int oc = p2pChannels.getInt("oc", 0);
+ if (DBG) logd(getName() + " set listen and operating channel");
+ if (mWifiNative.p2pSetChannel(lc, oc)) {
+ replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED);
+ }
+ break;
+ case WifiP2pManager.INITIATOR_REPORT_NFC_HANDOVER:
+ String handoverSelect = null;
+
+ if (message.obj != null) {
+ handoverSelect = ((Bundle) message.obj)
+ .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE);
+ }
+
+ if (handoverSelect != null
+ && mWifiNative.initiatorReportNfcHandover(handoverSelect)) {
+ replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED);
+ transitionTo(mGroupCreatingState);
+ } else {
+ replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED);
+ }
+ break;
+ case WifiP2pManager.RESPONDER_REPORT_NFC_HANDOVER:
+ String handoverRequest = null;
+
+ if (message.obj != null) {
+ handoverRequest = ((Bundle) message.obj)
+ .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE);
+ }
+
+ if (handoverRequest != null
+ && mWifiNative.responderReportNfcHandover(handoverRequest)) {
+ replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED);
+ transitionTo(mGroupCreatingState);
+ } else {
+ replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class GroupCreatingState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ sendMessageDelayed(obtainMessage(GROUP_CREATING_TIMED_OUT,
+ ++sGroupCreatingTimeoutIndex, 0), GROUP_CREATING_WAIT_TIME_MS);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ boolean ret = HANDLED;
+ switch (message.what) {
+ case GROUP_CREATING_TIMED_OUT:
+ if (sGroupCreatingTimeoutIndex == message.arg1) {
+ if (DBG) logd("Group negotiation timed out");
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ }
+ break;
+ case WifiP2pMonitor.P2P_DEVICE_LOST_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ WifiP2pDevice device = (WifiP2pDevice) message.obj;
+ if (!mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
+ if (DBG) {
+ logd("mSavedPeerConfig " + mSavedPeerConfig.deviceAddress
+ + "device " + device.deviceAddress);
+ }
+ // Do the regular device lost handling
+ ret = NOT_HANDLED;
+ break;
+ }
+ // Do nothing
+ if (DBG) logd("Add device to lost list " + device);
+ mPeersLostDuringConnection.updateSupplicantDetails(device);
+ break;
+ case WifiP2pManager.DISCOVER_PEERS:
+ // Discovery will break negotiation
+ replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
+ WifiP2pManager.BUSY);
+ break;
+ case WifiP2pManager.CANCEL_CONNECT:
+ // Do a supplicant p2p_cancel which only cancels an ongoing
+ // group negotiation. This will fail for a pending provision
+ // discovery or for a pending user action, but at the framework
+ // level, we always treat cancel as succeeded and enter
+ // an inactive state
+ mWifiNative.p2pCancelConnect();
handleGroupCreationFailure();
transitionTo(mInactiveState);
- }
- break;
- default:
- return NOT_HANDLED;
+ replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
+ break;
+ case WifiP2pMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
+ // We hit this scenario when NFC handover is invoked.
+ mAutonomousGroup = false;
+ transitionTo(mGroupNegotiationState);
+ break;
+ default:
+ ret = NOT_HANDLED;
+ }
+ return ret;
}
- return HANDLED;
}
- }
- class FrequencyConflictState extends State {
- private AlertDialog mFrequencyConflictDialog;
+ class UserAuthorizingNegotiationRequestState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ notifyInvitationReceived();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ boolean ret = HANDLED;
+ switch (message.what) {
+ case PEER_CONNECTION_USER_ACCEPT:
+ mWifiNative.p2pStopFind();
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
+ mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
+ sendPeersChangedBroadcast();
+ transitionTo(mGroupNegotiationState);
+ break;
+ case PEER_CONNECTION_USER_REJECT:
+ if (DBG) logd("User rejected negotiation " + mSavedPeerConfig);
+ transitionTo(mInactiveState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return ret;
+ }
+
+ @Override
+ public void exit() {
+ // TODO: dismiss dialog if not already done
+ }
+ }
+
+ class UserAuthorizingInviteRequestState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ notifyInvitationReceived();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ boolean ret = HANDLED;
+ switch (message.what) {
+ case PEER_CONNECTION_USER_ACCEPT:
+ mWifiNative.p2pStopFind();
+ if (!reinvokePersistentGroup(mSavedPeerConfig)) {
+ // Do negotiation when persistence fails
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
+ }
+ mPeers.updateStatus(mSavedPeerConfig.deviceAddress, WifiP2pDevice.INVITED);
+ sendPeersChangedBroadcast();
+ transitionTo(mGroupNegotiationState);
+ break;
+ case PEER_CONNECTION_USER_REJECT:
+ if (DBG) logd("User rejected invitation " + mSavedPeerConfig);
+ transitionTo(mInactiveState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return ret;
+ }
+
+ @Override
+ public void exit() {
+ // TODO: dismiss dialog if not already done
+ }
+ }
+
+ class ProvisionDiscoveryState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ mWifiNative.p2pProvisionDiscovery(mSavedPeerConfig);
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ WifiP2pProvDiscEvent provDisc = null;
+ WifiP2pDevice device = null;
+ switch (message.what) {
+ case WifiP2pMonitor.P2P_PROV_DISC_PBC_RSP_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Invalid argument(s)");
+ break;
+ }
+ provDisc = (WifiP2pProvDiscEvent) message.obj;
+ device = provDisc.device;
+ if (device != null
+ && !device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) {
+ break;
+ }
+ if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
+ if (DBG) logd("Found a match " + mSavedPeerConfig);
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
+ transitionTo(mGroupNegotiationState);
+ }
+ break;
+ case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ provDisc = (WifiP2pProvDiscEvent) message.obj;
+ device = provDisc.device;
+ if (device != null
+ && !device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) {
+ break;
+ }
+ if (mSavedPeerConfig.wps.setup == WpsInfo.KEYPAD) {
+ if (DBG) logd("Found a match " + mSavedPeerConfig);
+ // we already have the pin
+ if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) {
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
+ transitionTo(mGroupNegotiationState);
+ } else {
+ mJoinExistingGroup = false;
+ transitionTo(mUserAuthorizingNegotiationRequestState);
+ }
+ }
+ break;
+ case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ provDisc = (WifiP2pProvDiscEvent) message.obj;
+ device = provDisc.device;
+ if (device == null) {
+ Log.e(TAG, "Invalid device");
+ break;
+ }
+ if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) {
+ break;
+ }
+ if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) {
+ if (DBG) logd("Found a match " + mSavedPeerConfig);
+ mSavedPeerConfig.wps.pin = provDisc.pin;
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
+ notifyInvitationSent(provDisc.pin, device.deviceAddress);
+ transitionTo(mGroupNegotiationState);
+ }
+ break;
+ case WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT:
+ loge("provision discovery failed");
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class GroupNegotiationState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ // We ignore these right now, since we get a GROUP_STARTED notification
+ // afterwards
+ case WifiP2pMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
+ case WifiP2pMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
+ if (DBG) logd(getName() + " go success");
+ break;
+ case WifiP2pMonitor.P2P_GROUP_STARTED_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ mGroup = (WifiP2pGroup) message.obj;
+ if (DBG) logd(getName() + " group started");
+ if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
+ // update cache information and set network id to mGroup.
+ updatePersistentNetworks(NO_RELOAD);
+ String devAddr = mGroup.getOwner().deviceAddress;
+ mGroup.setNetworkId(mGroups.getNetworkId(devAddr,
+ mGroup.getNetworkName()));
+ }
+
+ if (mGroup.isGroupOwner()) {
+ // Setting an idle time out on GO causes issues with certain scenarios
+ // on clients where it can be off-channel for longer and with the power
+ // save modes used.
+ // TODO: Verify multi-channel scenarios and supplicant behavior are
+ // better before adding a time out in future
+ // Set group idle timeout of 10 sec, to avoid GO beaconing incase of any
+ // failure during 4-way Handshake.
+ if (!mAutonomousGroup) {
+ mWifiNative.setP2pGroupIdle(mGroup.getInterface(),
+ GROUP_IDLE_TIME_S);
+ }
+ startDhcpServer(mGroup.getInterface());
+ } else {
+ mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S);
+ startIpManager(mGroup.getInterface());
+ WifiP2pDevice groupOwner = mGroup.getOwner();
+ WifiP2pDevice peer = mPeers.get(groupOwner.deviceAddress);
+ if (peer != null) {
+ // update group owner details with peer details found at discovery
+ groupOwner.updateSupplicantDetails(peer);
+ mPeers.updateStatus(groupOwner.deviceAddress,
+ WifiP2pDevice.CONNECTED);
+ sendPeersChangedBroadcast();
+ } else {
+ // A supplicant bug can lead to reporting an invalid
+ // group owner address (all zeroes) at times. Avoid a
+ // crash, but continue group creation since it is not
+ // essential.
+ logw("Unknown group owner " + groupOwner);
+ }
+ }
+ transitionTo(mGroupCreatedState);
+ break;
+ case WifiP2pMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
+ P2pStatus status = (P2pStatus) message.obj;
+ if (status == P2pStatus.NO_COMMON_CHANNEL) {
+ transitionTo(mFrequencyConflictState);
+ break;
+ }
+ // continue with group removal handling
+ case WifiP2pMonitor.P2P_GROUP_REMOVED_EVENT:
+ if (DBG) logd(getName() + " go failure");
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ break;
+ case WifiP2pMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
+ // A group formation failure is always followed by
+ // a group removed event. Flushing things at group formation
+ // failure causes supplicant issues. Ignore right now.
+ status = (P2pStatus) message.obj;
+ if (status == P2pStatus.NO_COMMON_CHANNEL) {
+ transitionTo(mFrequencyConflictState);
+ break;
+ }
+ break;
+ case WifiP2pMonitor.P2P_INVITATION_RESULT_EVENT:
+ status = (P2pStatus) message.obj;
+ if (status == P2pStatus.SUCCESS) {
+ // invocation was succeeded.
+ // wait P2P_GROUP_STARTED_EVENT.
+ break;
+ }
+ loge("Invitation result " + status);
+ if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+ // target device has already removed the credential.
+ // So, remove this credential accordingly.
+ int netId = mSavedPeerConfig.netId;
+ if (netId >= 0) {
+ if (DBG) logd("Remove unknown client from the list");
+ removeClientFromList(netId, mSavedPeerConfig.deviceAddress, true);
+ }
+
+ // Reinvocation has failed, try group negotiation
+ mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
+ } else if (status == P2pStatus.INFORMATION_IS_CURRENTLY_UNAVAILABLE) {
+
+ // Devices setting persistent_reconnect to 0 in wpa_supplicant
+ // always defer the invocation request and return
+ // "information is currently unavailable" error.
+ // So, try another way to connect for interoperability.
+ mSavedPeerConfig.netId = WifiP2pGroup.PERSISTENT_NET_ID;
+ p2pConnectWithPinDisplay(mSavedPeerConfig);
+ } else if (status == P2pStatus.NO_COMMON_CHANNEL) {
+ transitionTo(mFrequencyConflictState);
+ } else {
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ class FrequencyConflictState extends State {
+ private AlertDialog mFrequencyConflictDialog;
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ notifyFrequencyConflict();
+ }
+
+ private void notifyFrequencyConflict() {
+ logd("Notify frequency conflict");
+ Resources r = Resources.getSystem();
+
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setMessage(r.getString(R.string.wifi_p2p_frequency_conflict_message,
+ getDeviceName(mSavedPeerConfig.deviceAddress)))
+ .setPositiveButton(r.getString(R.string.dlg_ok), new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ sendMessage(DROP_WIFI_USER_ACCEPT);
+ }
+ })
+ .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ sendMessage(DROP_WIFI_USER_REJECT);
+ }
+ })
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface arg0) {
+ sendMessage(DROP_WIFI_USER_REJECT);
+ }
+ })
+ .create();
+ dialog.setCanceledOnTouchOutside(false);
+
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
+ attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ dialog.getWindow().setAttributes(attrs);
+ dialog.show();
+ mFrequencyConflictDialog = dialog;
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case WifiP2pMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
+ case WifiP2pMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
+ loge(getName() + "group sucess during freq conflict!");
+ break;
+ case WifiP2pMonitor.P2P_GROUP_STARTED_EVENT:
+ loge(getName() + "group started after freq conflict, handle anyway");
+ deferMessage(message);
+ transitionTo(mGroupNegotiationState);
+ break;
+ case WifiP2pMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
+ case WifiP2pMonitor.P2P_GROUP_REMOVED_EVENT:
+ case WifiP2pMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
+ // Ignore failures since we retry again
+ break;
+ case DROP_WIFI_USER_REJECT:
+ // User rejected dropping wifi in favour of p2p
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ break;
+ case DROP_WIFI_USER_ACCEPT:
+ // User accepted dropping wifi in favour of p2p
+ if (mWifiChannel != null) {
+ mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 1);
+ } else {
+ loge("DROP_WIFI_USER_ACCEPT message received when WifiChannel is null");
+ }
+ mTemporarilyDisconnectedWifi = true;
+ break;
+ case DISCONNECT_WIFI_RESPONSE:
+ // Got a response from wifistatemachine, retry p2p
+ if (DBG) logd(getName() + "Wifi disconnected, retry p2p");
+ transitionTo(mInactiveState);
+ sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ public void exit() {
+ if (mFrequencyConflictDialog != null) mFrequencyConflictDialog.dismiss();
+ }
+ }
+
+ class GroupCreatedState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ // Once connected, peer config details are invalid
+ mSavedPeerConfig.invalidate();
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+
+ updateThisDevice(WifiP2pDevice.CONNECTED);
+
+ // DHCP server has already been started if I am a group owner
+ if (mGroup.isGroupOwner()) {
+ setWifiP2pInfoOnGroupFormation(
+ NetworkUtils.numericToInetAddress(SERVER_ADDRESS));
+ }
+
+ // In case of a negotiation group, connection changed is sent
+ // after a client joins. For autonomous, send now
+ if (mAutonomousGroup) {
+ sendP2pConnectionChangedBroadcast();
+ }
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ WifiP2pDevice device = null;
+ String deviceAddress = null;
+ switch (message.what) {
+ case WifiP2pMonitor.AP_STA_CONNECTED_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ device = (WifiP2pDevice) message.obj;
+ deviceAddress = device.deviceAddress;
+ // Clear timeout that was set when group was started.
+ mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
+ if (deviceAddress != null) {
+ if (mPeers.get(deviceAddress) != null) {
+ mGroup.addClient(mPeers.get(deviceAddress));
+ } else {
+ mGroup.addClient(deviceAddress);
+ }
+ mPeers.updateStatus(deviceAddress, WifiP2pDevice.CONNECTED);
+ if (DBG) logd(getName() + " ap sta connected");
+ sendPeersChangedBroadcast();
+ } else {
+ loge("Connect on null device address, ignore");
+ }
+ sendP2pConnectionChangedBroadcast();
+ break;
+ case WifiP2pMonitor.AP_STA_DISCONNECTED_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ break;
+ }
+ device = (WifiP2pDevice) message.obj;
+ deviceAddress = device.deviceAddress;
+ if (deviceAddress != null) {
+ mPeers.updateStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
+ if (mGroup.removeClient(deviceAddress)) {
+ if (DBG) logd("Removed client " + deviceAddress);
+ if (!mAutonomousGroup && mGroup.isClientListEmpty()) {
+ logd("Client list empty, remove non-persistent p2p group");
+ mWifiNative.p2pGroupRemove(mGroup.getInterface());
+ // We end up sending connection changed broadcast
+ // when this happens at exit()
+ } else {
+ // Notify when a client disconnects from group
+ sendP2pConnectionChangedBroadcast();
+ }
+ } else {
+ if (DBG) logd("Failed to remove client " + deviceAddress);
+ for (WifiP2pDevice c : mGroup.getClientList()) {
+ if (DBG) logd("client " + c.deviceAddress);
+ }
+ }
+ sendPeersChangedBroadcast();
+ if (DBG) logd(getName() + " ap sta disconnected");
+ } else {
+ loge("Disconnect on unknown device: " + device);
+ }
+ break;
+ case IPM_PRE_DHCP_ACTION:
+ mWifiNative.setP2pPowerSave(mGroup.getInterface(), false);
+ mIpManager.completedPreDhcpAction();
+ break;
+ case IPM_POST_DHCP_ACTION:
+ mWifiNative.setP2pPowerSave(mGroup.getInterface(), true);
+ break;
+ case IPM_DHCP_RESULTS:
+ mDhcpResults = (DhcpResults) message.obj;
+ break;
+ case IPM_PROVISIONING_SUCCESS:
+ if (DBG) logd("mDhcpResults: " + mDhcpResults);
+ if (mDhcpResults != null) {
+ setWifiP2pInfoOnGroupFormation(mDhcpResults.serverAddress);
+ }
+ sendP2pConnectionChangedBroadcast();
+ try {
+ final String ifname = mGroup.getInterface();
+ if (mDhcpResults != null) {
+ mNwService.addInterfaceToLocalNetwork(
+ ifname, mDhcpResults.getRoutes(ifname));
+ }
+ } catch (RemoteException e) {
+ loge("Failed to add iface to local network " + e);
+ }
+ break;
+ case IPM_PROVISIONING_FAILURE:
+ loge("IP provisioning failed");
+ mWifiNative.p2pGroupRemove(mGroup.getInterface());
+ break;
+ case WifiP2pManager.REMOVE_GROUP:
+ if (DBG) logd(getName() + " remove group");
+ if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
+ transitionTo(mOngoingGroupRemovalState);
+ replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
+ } else {
+ handleGroupRemoved();
+ transitionTo(mInactiveState);
+ replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ break;
+ case WifiP2pMonitor.P2P_GROUP_REMOVED_EVENT:
+ // We do not listen to NETWORK_DISCONNECTION_EVENT for group removal
+ // handling since supplicant actually tries to reconnect after a temporary
+ // disconnect until group idle time out. Eventually, a group removal event
+ // will come when group has been removed.
+ //
+ // When there are connectivity issues during temporary disconnect,
+ // the application will also just remove the group.
+ //
+ // Treating network disconnection as group removal causes race conditions
+ // since supplicant would still maintain the group at that stage.
+ if (DBG) logd(getName() + " group removed");
+ handleGroupRemoved();
+ transitionTo(mInactiveState);
+ break;
+ case WifiP2pMonitor.P2P_DEVICE_LOST_EVENT:
+ if (message.obj == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ return NOT_HANDLED;
+ }
+ device = (WifiP2pDevice) message.obj;
+ if (!mGroup.contains(device)) {
+ // do the regular device lost handling
+ return NOT_HANDLED;
+ }
+ // Device loss for a connected device indicates
+ // it is not in discovery any more
+ if (DBG) logd("Add device to lost list " + device);
+ mPeersLostDuringConnection.updateSupplicantDetails(device);
+ return HANDLED;
+ case WifiStateMachine.CMD_DISABLE_P2P_REQ:
+ sendMessage(WifiP2pManager.REMOVE_GROUP);
+ deferMessage(message);
+ break;
+ // This allows any client to join the GO during the
+ // WPS window
+ case WifiP2pManager.START_WPS:
+ WpsInfo wps = (WpsInfo) message.obj;
+ if (wps == null) {
+ replyToMessage(message, WifiP2pManager.START_WPS_FAILED);
+ break;
+ }
+ boolean ret = true;
+ if (wps.setup == WpsInfo.PBC) {
+ ret = mWifiNative.startWpsPbc(mGroup.getInterface(), null);
+ } else {
+ if (wps.pin == null) {
+ String pin = mWifiNative.startWpsPinDisplay(
+ mGroup.getInterface(), null);
+ try {
+ Integer.parseInt(pin);
+ notifyInvitationSent(pin, "any");
+ } catch (NumberFormatException ignore) {
+ ret = false;
+ }
+ } else {
+ ret = mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
+ wps.pin);
+ }
+ }
+ replyToMessage(message, ret ? WifiP2pManager.START_WPS_SUCCEEDED :
+ WifiP2pManager.START_WPS_FAILED);
+ break;
+ case WifiP2pManager.CONNECT:
+ WifiP2pConfig config = (WifiP2pConfig) message.obj;
+ if (isConfigInvalid(config)) {
+ loge("Dropping connect request " + config);
+ replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
+ break;
+ }
+ logd("Inviting device : " + config.deviceAddress);
+ mSavedPeerConfig = config;
+ if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
+ mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
+ sendPeersChangedBroadcast();
+ replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ // TODO: figure out updating the status to declined
+ // when invitation is rejected
+ break;
+ case WifiP2pMonitor.P2P_INVITATION_RESULT_EVENT:
+ P2pStatus status = (P2pStatus) message.obj;
+ if (status == P2pStatus.SUCCESS) {
+ // invocation was succeeded.
+ break;
+ }
+ loge("Invitation result " + status);
+ if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
+ // target device has already removed the credential.
+ // So, remove this credential accordingly.
+ int netId = mGroup.getNetworkId();
+ if (netId >= 0) {
+ if (DBG) logd("Remove unknown client from the list");
+ if (!removeClientFromList(netId,
+ mSavedPeerConfig.deviceAddress, false)) {
+ // not found the client on the list
+ loge("Already removed the client, ignore");
+ break;
+ }
+ // try invitation.
+ sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
+ }
+ }
+ break;
+ case WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
+ case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
+ case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
+ WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
+ mSavedPeerConfig = new WifiP2pConfig();
+ if (provDisc != null && provDisc.device != null) {
+ mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
+ }
+ if (message.what == WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
+ mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
+ } else if (message.what == WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
+ mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
+ mSavedPeerConfig.wps.pin = provDisc.pin;
+ } else {
+ mSavedPeerConfig.wps.setup = WpsInfo.PBC;
+ }
+ transitionTo(mUserAuthorizingJoinState);
+ break;
+ case WifiP2pMonitor.P2P_GROUP_STARTED_EVENT:
+ loge("Duplicate group creation event notice, ignore");
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ public void exit() {
+ updateThisDevice(WifiP2pDevice.AVAILABLE);
+ resetWifiP2pInfo();
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+ sendP2pConnectionChangedBroadcast();
+ }
+ }
+
+ class UserAuthorizingJoinState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ notifyInvitationReceived();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
+ case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
+ case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
+ // Ignore more client requests
+ break;
+ case PEER_CONNECTION_USER_ACCEPT:
+ // Stop discovery to avoid failure due to channel switch
+ mWifiNative.p2pStopFind();
+ if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
+ mWifiNative.startWpsPbc(mGroup.getInterface(), null);
+ } else {
+ mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
+ mSavedPeerConfig.wps.pin);
+ }
+ transitionTo(mGroupCreatedState);
+ break;
+ case PEER_CONNECTION_USER_REJECT:
+ if (DBG) logd("User rejected incoming request");
+ transitionTo(mGroupCreatedState);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ @Override
+ public void exit() {
+ // TODO: dismiss dialog if not already done
+ }
+ }
+
+ class OngoingGroupRemovalState extends State {
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ // Group removal ongoing. Multiple calls
+ // end up removing persisted network. Do nothing.
+ case WifiP2pManager.REMOVE_GROUP:
+ replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
+ break;
+ // Parent state will transition out of this state
+ // when removal is complete
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
@Override
- public void enter() {
- if (DBG) logd(getName());
- notifyFrequencyConflict();
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dump(fd, pw, args);
+ pw.println("mWifiP2pInfo " + mWifiP2pInfo);
+ pw.println("mGroup " + mGroup);
+ pw.println("mSavedPeerConfig " + mSavedPeerConfig);
+ pw.println("mGroups" + mGroups);
+ pw.println();
}
- private void notifyFrequencyConflict() {
- logd("Notify frequency conflict");
+ private void sendP2pStateChangedBroadcast(boolean enabled) {
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ if (enabled) {
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
+ WifiP2pManager.WIFI_P2P_STATE_ENABLED);
+ } else {
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
+ WifiP2pManager.WIFI_P2P_STATE_DISABLED);
+ }
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void sendP2pDiscoveryChangedBroadcast(boolean started) {
+ if (mDiscoveryStarted == started) return;
+ mDiscoveryStarted = started;
+
+ if (DBG) logd("discovery change broadcast " + started);
+
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, started
+ ? WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED :
+ WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void sendThisDeviceChangedBroadcast() {
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void sendPeersChangedBroadcast() {
+ final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
+ intent.putExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, new WifiP2pDeviceList(mPeers));
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void sendP2pConnectionChangedBroadcast() {
+ if (DBG) logd("sending p2p connection changed broadcast");
+ Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
+ intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
+ intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, new WifiP2pGroup(mGroup));
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ if (mWifiChannel != null) {
+ mWifiChannel.sendMessage(WifiP2pServiceImpl.P2P_CONNECTION_CHANGED,
+ new NetworkInfo(mNetworkInfo));
+ } else {
+ loge("sendP2pConnectionChangedBroadcast(): WifiChannel is null");
+ }
+ }
+
+ private void sendP2pPersistentGroupsChangedBroadcast() {
+ if (DBG) logd("sending p2p persistent groups changed broadcast");
+ Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void startDhcpServer(String intf) {
+ InterfaceConfiguration ifcg = null;
+ try {
+ ifcg = mNwService.getInterfaceConfig(intf);
+ ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(
+ SERVER_ADDRESS), 24));
+ ifcg.setInterfaceUp();
+ mNwService.setInterfaceConfig(intf, ifcg);
+ // This starts the dnsmasq server
+ ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ String[] tetheringDhcpRanges = cm.getTetheredDhcpRanges();
+ if (mNwService.isTetheringStarted()) {
+ if (DBG) logd("Stop existing tethering and restart it");
+ mNwService.stopTethering();
+ }
+ mNwService.tetherInterface(intf);
+ mNwService.startTethering(tetheringDhcpRanges);
+ } catch (Exception e) {
+ loge("Error configuring interface " + intf + ", :" + e);
+ return;
+ }
+
+ logd("Started Dhcp server on " + intf);
+ }
+
+ private void stopDhcpServer(String intf) {
+ try {
+ mNwService.untetherInterface(intf);
+ for (String temp : mNwService.listTetheredInterfaces()) {
+ logd("List all interfaces " + temp);
+ if (temp.compareTo(intf) != 0) {
+ logd("Found other tethering interfaces, so keep tethering alive");
+ return;
+ }
+ }
+ mNwService.stopTethering();
+ } catch (Exception e) {
+ loge("Error stopping Dhcp server" + e);
+ return;
+ } finally {
+ logd("Stopped Dhcp server");
+ }
+ }
+
+ private void notifyP2pEnableFailure() {
+ Resources r = Resources.getSystem();
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
+ .setMessage(r.getString(R.string.wifi_p2p_failed_message))
+ .setPositiveButton(r.getString(R.string.ok), null)
+ .create();
+ dialog.setCanceledOnTouchOutside(false);
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
+ attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ dialog.getWindow().setAttributes(attrs);
+ dialog.show();
+ }
+
+ private void addRowToDialog(ViewGroup group, int stringId, String value) {
+ Resources r = Resources.getSystem();
+ View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row,
+ group, false);
+ ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId));
+ ((TextView) row.findViewById(R.id.value)).setText(value);
+ group.addView(row);
+ }
+
+ private void notifyInvitationSent(String pin, String peerAddress) {
Resources r = Resources.getSystem();
+ final View textEntryView = LayoutInflater.from(mContext)
+ .inflate(R.layout.wifi_p2p_dialog, null);
+
+ ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
+ addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress));
+ addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin);
+
AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setMessage(r.getString(R.string.wifi_p2p_frequency_conflict_message,
- getDeviceName(mSavedPeerConfig.deviceAddress)))
- .setPositiveButton(r.getString(R.string.dlg_ok), new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- sendMessage(DROP_WIFI_USER_ACCEPT);
+ .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title))
+ .setView(textEntryView)
+ .setPositiveButton(r.getString(R.string.ok), null)
+ .create();
+ dialog.setCanceledOnTouchOutside(false);
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
+ attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ dialog.getWindow().setAttributes(attrs);
+ dialog.show();
+ }
+
+ private void notifyInvitationReceived() {
+ Resources r = Resources.getSystem();
+ final WpsInfo wps = mSavedPeerConfig.wps;
+ final View textEntryView = LayoutInflater.from(mContext)
+ .inflate(R.layout.wifi_p2p_dialog, null);
+
+ ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
+ addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName(
+ mSavedPeerConfig.deviceAddress));
+
+ final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin);
+
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title))
+ .setView(textEntryView)
+ .setPositiveButton(r.getString(R.string.accept), new OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (wps.setup == WpsInfo.KEYPAD) {
+ mSavedPeerConfig.wps.pin = pin.getText().toString();
+ }
+ if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig);
+ sendMessage(PEER_CONNECTION_USER_ACCEPT);
+ }
+ })
+ .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (DBG) logd(getName() + " ignore connect");
+ sendMessage(PEER_CONNECTION_USER_REJECT);
+ }
+ })
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface arg0) {
+ if (DBG) logd(getName() + " ignore connect");
+ sendMessage(PEER_CONNECTION_USER_REJECT);
+ }
+ })
+ .create();
+ dialog.setCanceledOnTouchOutside(false);
+
+ // make the enter pin area or the display pin area visible
+ switch (wps.setup) {
+ case WpsInfo.KEYPAD:
+ if (DBG) logd("Enter pin section visible");
+ textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE);
+ break;
+ case WpsInfo.DISPLAY:
+ if (DBG) logd("Shown pin section visible");
+ addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin);
+ break;
+ default:
+ break;
+ }
+
+ if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_APPLIANCE)
+ == Configuration.UI_MODE_TYPE_APPLIANCE) {
+ // For appliance devices, add a key listener which accepts.
+ dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
+
+ @Override
+ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+ // TODO: make the actual key come from a config value.
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+ sendMessage(PEER_CONNECTION_USER_ACCEPT);
+ dialog.dismiss();
+ return true;
}
- })
- .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- sendMessage(DROP_WIFI_USER_REJECT);
- }
- })
- .setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface arg0) {
- sendMessage(DROP_WIFI_USER_REJECT);
- }
- })
- .create();
+ return false;
+ }
+ });
+ // TODO: add timeout for this dialog.
+ // TODO: update UI in appliance mode to tell user what to do.
+ }
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
dialog.getWindow().setAttributes(attrs);
dialog.show();
- mFrequencyConflictDialog = dialog;
}
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
- case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
- loge(getName() + "group sucess during freq conflict!");
- break;
- case WifiMonitor.P2P_GROUP_STARTED_EVENT:
- loge(getName() + "group started after freq conflict, handle anyway");
- deferMessage(message);
- transitionTo(mGroupNegotiationState);
- break;
- case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
- case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
- case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
- // Ignore failures since we retry again
- break;
- case DROP_WIFI_USER_REJECT:
- // User rejected dropping wifi in favour of p2p
- handleGroupCreationFailure();
- transitionTo(mInactiveState);
- break;
- case DROP_WIFI_USER_ACCEPT:
- // User accepted dropping wifi in favour of p2p
- mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 1);
- mTemporarilyDisconnectedWifi = true;
- break;
- case DISCONNECT_WIFI_RESPONSE:
- // Got a response from wifistatemachine, retry p2p
- if (DBG) logd(getName() + "Wifi disconnected, retry p2p");
- transitionTo(mInactiveState);
- sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
+ /**
+ * This method unifies the persisent group list, cleans up unused
+ * networks and if required, updates corresponding broadcast receivers
+ * @param boolean if true, reload the group list from scratch
+ * and send broadcast message with fresh list
+ */
+ private void updatePersistentNetworks(boolean reload) {
+ if (reload) mGroups.clear();
- public void exit() {
- if (mFrequencyConflictDialog != null) mFrequencyConflictDialog.dismiss();
- }
- }
-
- class GroupCreatedState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- // Once connected, peer config details are invalid
- mSavedPeerConfig.invalidate();
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
-
- updateThisDevice(WifiP2pDevice.CONNECTED);
-
- //DHCP server has already been started if I am a group owner
- if (mGroup.isGroupOwner()) {
- setWifiP2pInfoOnGroupFormation(NetworkUtils.numericToInetAddress(SERVER_ADDRESS));
- }
-
- // In case of a negotiation group, connection changed is sent
- // after a client joins. For autonomous, send now
- if (mAutonomousGroup) {
- sendP2pConnectionChangedBroadcast();
- }
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiMonitor.AP_STA_CONNECTED_EVENT:
- WifiP2pDevice device = (WifiP2pDevice) message.obj;
- String deviceAddress = device.deviceAddress;
- // Clear timeout that was set when group was started.
- mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
- if (deviceAddress != null) {
- if (mPeers.get(deviceAddress) != null) {
- mGroup.addClient(mPeers.get(deviceAddress));
- } else {
- mGroup.addClient(deviceAddress);
- }
- mPeers.updateStatus(deviceAddress, WifiP2pDevice.CONNECTED);
- if (DBG) logd(getName() + " ap sta connected");
- sendPeersChangedBroadcast();
- } else {
- loge("Connect on null device address, ignore");
+ // Save in all cases, including when reload was requested, but
+ // no network has been found.
+ if (mWifiNative.p2pListNetworks(mGroups) || reload) {
+ for (WifiP2pGroup group : mGroups.getGroupList()) {
+ if (mThisDevice.deviceAddress.equals(group.getOwner().deviceAddress)) {
+ group.setOwner(mThisDevice);
}
- sendP2pConnectionChangedBroadcast();
- break;
- case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
- device = (WifiP2pDevice) message.obj;
- deviceAddress = device.deviceAddress;
- if (deviceAddress != null) {
- mPeers.updateStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
- if (mGroup.removeClient(deviceAddress)) {
- if (DBG) logd("Removed client " + deviceAddress);
- if (!mAutonomousGroup && mGroup.isClientListEmpty()) {
- logd("Client list empty, remove non-persistent p2p group");
- mWifiNative.p2pGroupRemove(mGroup.getInterface());
- // We end up sending connection changed broadcast
- // when this happens at exit()
- } else {
- // Notify when a client disconnects from group
- sendP2pConnectionChangedBroadcast();
- }
- } else {
- if (DBG) logd("Failed to remove client " + deviceAddress);
- for (WifiP2pDevice c : mGroup.getClientList()) {
- if (DBG) logd("client " + c.deviceAddress);
- }
- }
- sendPeersChangedBroadcast();
- if (DBG) logd(getName() + " ap sta disconnected");
- } else {
- loge("Disconnect on unknown device: " + device);
- }
- break;
- case IPM_PRE_DHCP_ACTION:
- mWifiNative.setP2pPowerSave(mGroup.getInterface(), false);
- mIpManager.completedPreDhcpAction();
- break;
- case IPM_POST_DHCP_ACTION:
- mWifiNative.setP2pPowerSave(mGroup.getInterface(), true);
- break;
- case IPM_DHCP_RESULTS:
- mDhcpResults = (DhcpResults) message.obj;
- break;
- case IPM_PROVISIONING_SUCCESS:
- if (DBG) logd("mDhcpResults: " + mDhcpResults);
- setWifiP2pInfoOnGroupFormation(mDhcpResults.serverAddress);
- sendP2pConnectionChangedBroadcast();
- try {
- final String ifname = mGroup.getInterface();
- mNwService.addInterfaceToLocalNetwork(
- ifname, mDhcpResults.getRoutes(ifname));
- } catch (RemoteException e) {
- loge("Failed to add iface to local network " + e);
- }
- break;
- case IPM_PROVISIONING_FAILURE:
- loge("IP provisioning failed");
- mWifiNative.p2pGroupRemove(mGroup.getInterface());
- break;
- case WifiP2pManager.REMOVE_GROUP:
- if (DBG) logd(getName() + " remove group");
- if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) {
- transitionTo(mOngoingGroupRemovalState);
- replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
- } else {
- handleGroupRemoved();
- transitionTo(mInactiveState);
- replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
- WifiP2pManager.ERROR);
- }
- break;
- /* We do not listen to NETWORK_DISCONNECTION_EVENT for group removal
- * handling since supplicant actually tries to reconnect after a temporary
- * disconnect until group idle time out. Eventually, a group removal event
- * will come when group has been removed.
- *
- * When there are connectivity issues during temporary disconnect, the application
- * will also just remove the group.
- *
- * Treating network disconnection as group removal causes race conditions since
- * supplicant would still maintain the group at that stage.
- */
- case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
- if (DBG) logd(getName() + " group removed");
- handleGroupRemoved();
- transitionTo(mInactiveState);
- break;
- case WifiMonitor.P2P_DEVICE_LOST_EVENT:
- device = (WifiP2pDevice) message.obj;
- //Device loss for a connected device indicates it is not in discovery any more
- if (mGroup.contains(device)) {
- if (DBG) logd("Add device to lost list " + device);
- mPeersLostDuringConnection.updateSupplicantDetails(device);
- return HANDLED;
- }
- // Do the regular device lost handling
- return NOT_HANDLED;
- case WifiStateMachine.CMD_DISABLE_P2P_REQ:
- sendMessage(WifiP2pManager.REMOVE_GROUP);
- deferMessage(message);
- break;
- // This allows any client to join the GO during the
- // WPS window
- case WifiP2pManager.START_WPS:
- WpsInfo wps = (WpsInfo) message.obj;
- if (wps == null) {
- replyToMessage(message, WifiP2pManager.START_WPS_FAILED);
- break;
- }
- boolean ret = true;
- if (wps.setup == WpsInfo.PBC) {
- ret = mWifiNative.startWpsPbc(mGroup.getInterface(), null);
- } else {
- if (wps.pin == null) {
- String pin = mWifiNative.startWpsPinDisplay(mGroup.getInterface());
- try {
- Integer.parseInt(pin);
- notifyInvitationSent(pin, "any");
- } catch (NumberFormatException ignore) {
- ret = false;
- }
- } else {
- ret = mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
- wps.pin);
- }
- }
- replyToMessage(message, ret ? WifiP2pManager.START_WPS_SUCCEEDED :
- WifiP2pManager.START_WPS_FAILED);
- break;
- case WifiP2pManager.CONNECT:
- WifiP2pConfig config = (WifiP2pConfig) message.obj;
- if (isConfigInvalid(config)) {
- loge("Dropping connect requeset " + config);
- replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
- break;
- }
- logd("Inviting device : " + config.deviceAddress);
- mSavedPeerConfig = config;
- if (mWifiNative.p2pInvite(mGroup, config.deviceAddress)) {
- mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED);
- sendPeersChangedBroadcast();
- replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
- } else {
- replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
- WifiP2pManager.ERROR);
- }
- // TODO: figure out updating the status to declined when invitation is rejected
- break;
- case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
- P2pStatus status = (P2pStatus)message.obj;
- if (status == P2pStatus.SUCCESS) {
- // invocation was succeeded.
- break;
- }
- loge("Invitation result " + status);
- if (status == P2pStatus.UNKNOWN_P2P_GROUP) {
- // target device has already removed the credential.
- // So, remove this credential accordingly.
- int netId = mGroup.getNetworkId();
- if (netId >= 0) {
- if (DBG) logd("Remove unknown client from the list");
- if (!removeClientFromList(netId,
- mSavedPeerConfig.deviceAddress, false)) {
- // not found the client on the list
- loge("Already removed the client, ignore");
- break;
- }
- // try invitation.
- sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
- }
- }
- break;
- case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
- case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
- case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
- WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj;
- mSavedPeerConfig = new WifiP2pConfig();
- mSavedPeerConfig.deviceAddress = provDisc.device.deviceAddress;
- if (message.what == WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT) {
- mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD;
- } else if (message.what == WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) {
- mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY;
- mSavedPeerConfig.wps.pin = provDisc.pin;
- } else {
- mSavedPeerConfig.wps.setup = WpsInfo.PBC;
- }
- transitionTo(mUserAuthorizingJoinState);
- break;
- case WifiMonitor.P2P_GROUP_STARTED_EVENT:
- loge("Duplicate group creation event notice, ignore");
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
-
- public void exit() {
- updateThisDevice(WifiP2pDevice.AVAILABLE);
- resetWifiP2pInfo();
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
- sendP2pConnectionChangedBroadcast();
- }
- }
-
- class UserAuthorizingJoinState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- notifyInvitationReceived();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
- case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
- case WifiMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT:
- //Ignore more client requests
- break;
- case PEER_CONNECTION_USER_ACCEPT:
- //Stop discovery to avoid failure due to channel switch
- mWifiNative.p2pStopFind();
- if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) {
- mWifiNative.startWpsPbc(mGroup.getInterface(), null);
- } else {
- mWifiNative.startWpsPinKeypad(mGroup.getInterface(),
- mSavedPeerConfig.wps.pin);
- }
- transitionTo(mGroupCreatedState);
- break;
- case PEER_CONNECTION_USER_REJECT:
- if (DBG) logd("User rejected incoming request");
- transitionTo(mGroupCreatedState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
-
- @Override
- public void exit() {
- //TODO: dismiss dialog if not already done
- }
- }
-
- class OngoingGroupRemovalState extends State {
- @Override
- public void enter() {
- if (DBG) logd(getName());
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) logd(getName() + message.toString());
- switch (message.what) {
- // Group removal ongoing. Multiple calls
- // end up removing persisted network. Do nothing.
- case WifiP2pManager.REMOVE_GROUP:
- replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
- break;
- // Parent state will transition out of this state
- // when removal is complete
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- super.dump(fd, pw, args);
- pw.println("mWifiP2pInfo " + mWifiP2pInfo);
- pw.println("mGroup " + mGroup);
- pw.println("mSavedPeerConfig " + mSavedPeerConfig);
- pw.println();
- }
-
- private void sendP2pStateChangedBroadcast(boolean enabled) {
- final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- if (enabled) {
- intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
- WifiP2pManager.WIFI_P2P_STATE_ENABLED);
- } else {
- intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
- WifiP2pManager.WIFI_P2P_STATE_DISABLED);
- }
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private void sendP2pDiscoveryChangedBroadcast(boolean started) {
- if (mDiscoveryStarted == started) return;
- mDiscoveryStarted = started;
-
- if (DBG) logd("discovery change broadcast " + started);
-
- final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE, started ?
- WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED :
- WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private void sendThisDeviceChangedBroadcast() {
- final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private void sendPeersChangedBroadcast() {
- final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
- intent.putExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, new WifiP2pDeviceList(mPeers));
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private void sendP2pConnectionChangedBroadcast() {
- if (DBG) logd("sending p2p connection changed broadcast");
- Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- | Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
- intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
- intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, new WifiP2pGroup(mGroup));
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- mWifiChannel.sendMessage(WifiP2pServiceImpl.P2P_CONNECTION_CHANGED,
- new NetworkInfo(mNetworkInfo));
- }
-
- private void sendP2pPersistentGroupsChangedBroadcast() {
- if (DBG) logd("sending p2p persistent groups changed broadcast");
- Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private void startDhcpServer(String intf) {
- InterfaceConfiguration ifcg = null;
- try {
- ifcg = mNwService.getInterfaceConfig(intf);
- ifcg.setLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(
- SERVER_ADDRESS), 24));
- ifcg.setInterfaceUp();
- mNwService.setInterfaceConfig(intf, ifcg);
- /* This starts the dnsmasq server */
- ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- String[] tetheringDhcpRanges = cm.getTetheredDhcpRanges();
- if (mNwService.isTetheringStarted()) {
- if (DBG) logd("Stop existing tethering and restart it");
- mNwService.stopTethering();
- }
- mNwService.tetherInterface(intf);
- mNwService.startTethering(tetheringDhcpRanges);
- } catch (Exception e) {
- loge("Error configuring interface " + intf + ", :" + e);
- return;
- }
-
- logd("Started Dhcp server on " + intf);
- }
-
- private void stopDhcpServer(String intf) {
- try {
- mNwService.untetherInterface(intf);
- for (String temp : mNwService.listTetheredInterfaces()) {
- logd("List all interfaces " + temp);
- if (temp.compareTo(intf) != 0) {
- logd("Found other tethering interfaces, so keep tethering alive");
- return;
}
+ mWifiNative.saveConfig();
+ sendP2pPersistentGroupsChangedBroadcast();
}
- mNwService.stopTethering();
- } catch (Exception e) {
- loge("Error stopping Dhcp server" + e);
- return;
- } finally {
- logd("Stopped Dhcp server");
- }
- }
-
- private void notifyP2pEnableFailure() {
- Resources r = Resources.getSystem();
- AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
- .setMessage(r.getString(R.string.wifi_p2p_failed_message))
- .setPositiveButton(r.getString(R.string.ok), null)
- .create();
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
- attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- dialog.getWindow().setAttributes(attrs);
- dialog.show();
- }
-
- private void addRowToDialog(ViewGroup group, int stringId, String value) {
- Resources r = Resources.getSystem();
- View row = LayoutInflater.from(mContext).inflate(R.layout.wifi_p2p_dialog_row,
- group, false);
- ((TextView) row.findViewById(R.id.name)).setText(r.getString(stringId));
- ((TextView) row.findViewById(R.id.value)).setText(value);
- group.addView(row);
- }
-
- private void notifyInvitationSent(String pin, String peerAddress) {
- Resources r = Resources.getSystem();
-
- final View textEntryView = LayoutInflater.from(mContext)
- .inflate(R.layout.wifi_p2p_dialog, null);
-
- ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
- addRowToDialog(group, R.string.wifi_p2p_to_message, getDeviceName(peerAddress));
- addRowToDialog(group, R.string.wifi_p2p_show_pin_message, pin);
-
- AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.wifi_p2p_invitation_sent_title))
- .setView(textEntryView)
- .setPositiveButton(r.getString(R.string.ok), null)
- .create();
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
- attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- dialog.getWindow().setAttributes(attrs);
- dialog.show();
- }
-
- private void notifyInvitationReceived() {
- Resources r = Resources.getSystem();
- final WpsInfo wps = mSavedPeerConfig.wps;
- final View textEntryView = LayoutInflater.from(mContext)
- .inflate(R.layout.wifi_p2p_dialog, null);
-
- ViewGroup group = (ViewGroup) textEntryView.findViewById(R.id.info);
- addRowToDialog(group, R.string.wifi_p2p_from_message, getDeviceName(
- mSavedPeerConfig.deviceAddress));
-
- final EditText pin = (EditText) textEntryView.findViewById(R.id.wifi_p2p_wps_pin);
-
- AlertDialog dialog = new AlertDialog.Builder(mContext)
- .setTitle(r.getString(R.string.wifi_p2p_invitation_to_connect_title))
- .setView(textEntryView)
- .setPositiveButton(r.getString(R.string.accept), new OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (wps.setup == WpsInfo.KEYPAD) {
- mSavedPeerConfig.wps.pin = pin.getText().toString();
- }
- if (DBG) logd(getName() + " accept invitation " + mSavedPeerConfig);
- sendMessage(PEER_CONNECTION_USER_ACCEPT);
- }
- })
- .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (DBG) logd(getName() + " ignore connect");
- sendMessage(PEER_CONNECTION_USER_REJECT);
- }
- })
- .setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface arg0) {
- if (DBG) logd(getName() + " ignore connect");
- sendMessage(PEER_CONNECTION_USER_REJECT);
- }
- })
- .create();
-
- //make the enter pin area or the display pin area visible
- switch (wps.setup) {
- case WpsInfo.KEYPAD:
- if (DBG) logd("Enter pin section visible");
- textEntryView.findViewById(R.id.enter_pin_section).setVisibility(View.VISIBLE);
- break;
- case WpsInfo.DISPLAY:
- if (DBG) logd("Shown pin section visible");
- addRowToDialog(group, R.string.wifi_p2p_show_pin_message, wps.pin);
- break;
- default:
- break;
}
- if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_APPLIANCE) ==
- Configuration.UI_MODE_TYPE_APPLIANCE) {
- // For appliance devices, add a key listener which accepts.
- dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
-
- @Override
- public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
- // TODO: make the actual key come from a config value.
- if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
- sendMessage(PEER_CONNECTION_USER_ACCEPT);
- dialog.dismiss();
- return true;
- }
- return false;
- }
- });
- // TODO: add timeout for this dialog.
- // TODO: update UI in appliance mode to tell user what to do.
+ /**
+ * A config is valid if it has a peer address that has already been
+ * discovered
+ * @param WifiP2pConfig config to be validated
+ * @return true if it is invalid, false otherwise
+ */
+ private boolean isConfigInvalid(WifiP2pConfig config) {
+ if (config == null) return true;
+ if (TextUtils.isEmpty(config.deviceAddress)) return true;
+ if (mPeers.get(config.deviceAddress) == null) return true;
+ return false;
}
- dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
- attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
- dialog.getWindow().setAttributes(attrs);
- dialog.show();
- }
+ private WifiP2pDevice fetchCurrentDeviceDetails(WifiP2pConfig config) {
+ if (config == null) return null;
+ // Fetch & update group capability from supplicant on the device
+ int gc = mWifiNative.getGroupCapability(config.deviceAddress);
+ // TODO: The supplicant does not provide group capability changes as an event.
+ // Having it pushed as an event would avoid polling for this information right
+ // before a connection
+ mPeers.updateGroupCapability(config.deviceAddress, gc);
+ return mPeers.get(config.deviceAddress);
+ }
- /**
- * Synchronize the persistent group list between
- * wpa_supplicant and mGroups.
- */
- private void updatePersistentNetworks(boolean reload) {
- String listStr = mWifiNative.listNetworks();
- if (listStr == null) return;
-
- boolean isSaveRequired = false;
- String[] lines = listStr.split("\n");
- if (lines == null) return;
-
- if (reload) mGroups.clear();
-
- // Skip the first line, which is a header
- for (int i = 1; i < lines.length; i++) {
- String[] result = lines[i].split("\t");
- if (result == null || result.length < 4) {
- continue;
+ /**
+ * Start a p2p group negotiation and display pin if necessary
+ * @param config for the peer
+ */
+ private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
+ if (config == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ return;
}
- // network-id | ssid | bssid | flags
- int netId = -1;
- String ssid = result[1];
- String bssid = result[2];
- String flags = result[3];
+ WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
+ if (dev == null) {
+ Log.e(TAG, "Invalid device");
+ return;
+ }
+ String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
try {
- netId = Integer.parseInt(result[0]);
- } catch(NumberFormatException e) {
- e.printStackTrace();
- continue;
+ Integer.parseInt(pin);
+ notifyInvitationSent(pin, config.deviceAddress);
+ } catch (NumberFormatException ignore) {
+ // do nothing if p2pConnect did not return a pin
}
-
- if (flags.indexOf("[CURRENT]") != -1) {
- continue;
- }
- if (flags.indexOf("[P2P-PERSISTENT]") == -1) {
- /*
- * The unused profile is sometimes remained when the p2p group formation is failed.
- * So, we clean up the p2p group here.
- */
- if (DBG) logd("clean up the unused persistent group. netId=" + netId);
- mWifiNative.removeNetwork(netId);
- isSaveRequired = true;
- continue;
- }
-
- if (mGroups.contains(netId)) {
- continue;
- }
-
- WifiP2pGroup group = new WifiP2pGroup();
- group.setNetworkId(netId);
- group.setNetworkName(ssid);
- String mode = mWifiNative.getNetworkVariable(netId, "mode");
- if (mode != null && mode.equals("3")) {
- group.setIsGroupOwner(true);
- }
- if (bssid.equalsIgnoreCase(mThisDevice.deviceAddress)) {
- group.setOwner(mThisDevice);
- } else {
- WifiP2pDevice device = new WifiP2pDevice();
- device.deviceAddress = bssid;
- group.setOwner(device);
- }
- mGroups.add(group);
- isSaveRequired = true;
}
- if (reload || isSaveRequired) {
- mWifiNative.saveConfig();
- sendP2pPersistentGroupsChangedBroadcast();
- }
- }
+ /**
+ * Reinvoke a persistent group.
+ *
+ * @param config for the peer
+ * @return true on success, false on failure
+ */
+ private boolean reinvokePersistentGroup(WifiP2pConfig config) {
+ if (config == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ return false;
+ }
+ WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
+ if (dev == null) {
+ Log.e(TAG, "Invalid device");
+ return false;
+ }
+ boolean join = dev.isGroupOwner();
+ String ssid = mWifiNative.p2pGetSsid(dev.deviceAddress);
+ if (DBG) logd("target ssid is " + ssid + " join:" + join);
- /**
- * A config is valid if it has a peer address that has already been
- * discovered
- * @return true if it is invalid, false otherwise
- */
- private boolean isConfigInvalid(WifiP2pConfig config) {
- if (config == null) return true;
- if (TextUtils.isEmpty(config.deviceAddress)) return true;
- if (mPeers.get(config.deviceAddress) == null) return true;
- return false;
- }
+ if (join && dev.isGroupLimit()) {
+ if (DBG) logd("target device reaches group limit.");
- /* TODO: The supplicant does not provide group capability changes as an event.
- * Having it pushed as an event would avoid polling for this information right
- * before a connection
- */
- private WifiP2pDevice fetchCurrentDeviceDetails(WifiP2pConfig config) {
- /* Fetch & update group capability from supplicant on the device */
- int gc = mWifiNative.getGroupCapability(config.deviceAddress);
- mPeers.updateGroupCapability(config.deviceAddress, gc);
- return mPeers.get(config.deviceAddress);
- }
-
- /**
- * Start a p2p group negotiation and display pin if necessary
- * @param config for the peer
- */
- private void p2pConnectWithPinDisplay(WifiP2pConfig config) {
- WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
-
- String pin = mWifiNative.p2pConnect(config, dev.isGroupOwner());
- try {
- Integer.parseInt(pin);
- notifyInvitationSent(pin, config.deviceAddress);
- } catch (NumberFormatException ignore) {
- // do nothing if p2pConnect did not return a pin
- }
- }
-
- /**
- * Reinvoke a persistent group.
- *
- * @param config for the peer
- * @return true on success, false on failure
- */
- private boolean reinvokePersistentGroup(WifiP2pConfig config) {
- WifiP2pDevice dev = fetchCurrentDeviceDetails(config);
-
- boolean join = dev.isGroupOwner();
- String ssid = mWifiNative.p2pGetSsid(dev.deviceAddress);
- if (DBG) logd("target ssid is " + ssid + " join:" + join);
-
- if (join && dev.isGroupLimit()) {
- if (DBG) logd("target device reaches group limit.");
-
- // if the target group has reached the limit,
- // try group formation.
- join = false;
- } else if (join) {
- int netId = mGroups.getNetworkId(dev.deviceAddress, ssid);
- if (netId >= 0) {
- // Skip WPS and start 4way handshake immediately.
- if (!mWifiNative.p2pGroupAdd(netId)) {
- return false;
+ // if the target group has reached the limit,
+ // try group formation.
+ join = false;
+ } else if (join) {
+ int netId = mGroups.getNetworkId(dev.deviceAddress, ssid);
+ if (netId >= 0) {
+ // Skip WPS and start 4way handshake immediately.
+ if (!mWifiNative.p2pGroupAdd(netId)) {
+ return false;
+ }
+ return true;
}
+ }
+
+ if (!join && dev.isDeviceLimit()) {
+ loge("target device reaches the device limit.");
+ return false;
+ }
+
+ if (!join && dev.isInvitationCapable()) {
+ int netId = WifiP2pGroup.PERSISTENT_NET_ID;
+ if (config.netId >= 0) {
+ if (config.deviceAddress.equals(mGroups.getOwnerAddr(config.netId))) {
+ netId = config.netId;
+ }
+ } else {
+ netId = mGroups.getNetworkId(dev.deviceAddress);
+ }
+ if (netId < 0) {
+ netId = getNetworkIdFromClientList(dev.deviceAddress);
+ }
+ if (DBG) logd("netId related with " + dev.deviceAddress + " = " + netId);
+ if (netId >= 0) {
+ // Invoke the persistent group.
+ if (mWifiNative.p2pReinvoke(netId, dev.deviceAddress)) {
+ // Save network id. It'll be used when an invitation
+ // result event is received.
+ config.netId = netId;
+ return true;
+ } else {
+ loge("p2pReinvoke() failed, update networks");
+ updatePersistentNetworks(RELOAD);
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the network id of the group owner profile which has the p2p client with
+ * the specified device address in it's client list.
+ * If more than one persistent group of the same address is present in its client
+ * lists, return the first one.
+ *
+ * @param deviceAddress p2p device address.
+ * @return the network id. if not found, return -1.
+ */
+ private int getNetworkIdFromClientList(String deviceAddress) {
+ if (deviceAddress == null) return -1;
+
+ Collection<WifiP2pGroup> groups = mGroups.getGroupList();
+ for (WifiP2pGroup group : groups) {
+ int netId = group.getNetworkId();
+ String[] p2pClientList = getClientList(netId);
+ if (p2pClientList == null) continue;
+ for (String client : p2pClientList) {
+ if (deviceAddress.equalsIgnoreCase(client)) {
+ return netId;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return p2p client list associated with the specified network id.
+ * @param netId network id.
+ * @return p2p client list. if not found, return null.
+ */
+ private String[] getClientList(int netId) {
+ String p2pClients = mWifiNative.getP2pClientList(netId);
+ if (p2pClients == null) {
+ return null;
+ }
+ return p2pClients.split(" ");
+ }
+
+ /**
+ * Remove the specified p2p client from the specified profile.
+ * @param netId network id of the profile.
+ * @param addr p2p client address to be removed.
+ * @param isRemovable if true, remove the specified profile if its client
+ * list becomes empty.
+ * @return whether removing the specified p2p client is successful or not.
+ */
+ private boolean removeClientFromList(int netId, String addr, boolean isRemovable) {
+ StringBuilder modifiedClientList = new StringBuilder();
+ String[] currentClientList = getClientList(netId);
+ boolean isClientRemoved = false;
+ if (currentClientList != null) {
+ for (String client : currentClientList) {
+ if (!client.equalsIgnoreCase(addr)) {
+ modifiedClientList.append(" ");
+ modifiedClientList.append(client);
+ } else {
+ isClientRemoved = true;
+ }
+ }
+ }
+ if (modifiedClientList.length() == 0 && isRemovable) {
+ // the client list is empty. so remove it.
+ if (DBG) logd("Remove unknown network");
+ mGroups.remove(netId);
return true;
}
- }
- if (!join && dev.isDeviceLimit()) {
- loge("target device reaches the device limit.");
- return false;
- }
-
- if (!join && dev.isInvitationCapable()) {
- int netId = WifiP2pGroup.PERSISTENT_NET_ID;
- if (config.netId >= 0) {
- if (config.deviceAddress.equals(mGroups.getOwnerAddr(config.netId))) {
- netId = config.netId;
- }
- } else {
- netId = mGroups.getNetworkId(dev.deviceAddress);
+ if (!isClientRemoved) {
+ // specified p2p client is not found. already removed.
+ return false;
}
- if (netId < 0) {
- netId = getNetworkIdFromClientList(dev.deviceAddress);
+
+ if (DBG) logd("Modified client list: " + modifiedClientList);
+ if (modifiedClientList.length() == 0) {
+ modifiedClientList.append("\"\"");
}
- if (DBG) logd("netId related with " + dev.deviceAddress + " = " + netId);
- if (netId >= 0) {
- // Invoke the persistent group.
- if (mWifiNative.p2pReinvoke(netId, dev.deviceAddress)) {
- // Save network id. It'll be used when an invitation result event is received.
- config.netId = netId;
- return true;
- } else {
- loge("p2pReinvoke() failed, update networks");
- updatePersistentNetworks(RELOAD);
- return false;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Return the network id of the group owner profile which has the p2p client with
- * the specified device address in it's client list.
- * If more than one persistent group of the same address is present in its client
- * lists, return the first one.
- *
- * @param deviceAddress p2p device address.
- * @return the network id. if not found, return -1.
- */
- private int getNetworkIdFromClientList(String deviceAddress) {
- if (deviceAddress == null) return -1;
-
- Collection<WifiP2pGroup> groups = mGroups.getGroupList();
- for (WifiP2pGroup group : groups) {
- int netId = group.getNetworkId();
- String[] p2pClientList = getClientList(netId);
- if (p2pClientList == null) continue;
- for (String client : p2pClientList) {
- if (deviceAddress.equalsIgnoreCase(client)) {
- return netId;
- }
- }
- }
- return -1;
- }
-
- /**
- * Return p2p client list associated with the specified network id.
- * @param netId network id.
- * @return p2p client list. if not found, return null.
- */
- private String[] getClientList(int netId) {
- String p2pClients = mWifiNative.getNetworkVariable(netId, "p2p_client_list");
- if (p2pClients == null) {
- return null;
- }
- return p2pClients.split(" ");
- }
-
- /**
- * Remove the specified p2p client from the specified profile.
- * @param netId network id of the profile.
- * @param addr p2p client address to be removed.
- * @param isRemovable if true, remove the specified profile if its client list becomes empty.
- * @return whether removing the specified p2p client is successful or not.
- */
- private boolean removeClientFromList(int netId, String addr, boolean isRemovable) {
- StringBuilder modifiedClientList = new StringBuilder();
- String[] currentClientList = getClientList(netId);
- boolean isClientRemoved = false;
- if (currentClientList != null) {
- for (String client : currentClientList) {
- if (!client.equalsIgnoreCase(addr)) {
- modifiedClientList.append(" ");
- modifiedClientList.append(client);
- } else {
- isClientRemoved = true;
- }
- }
- }
- if (modifiedClientList.length() == 0 && isRemovable) {
- // the client list is empty. so remove it.
- if (DBG) logd("Remove unknown network");
- mGroups.remove(netId);
+ mWifiNative.setP2pClientList(netId, modifiedClientList.toString());
+ mWifiNative.saveConfig();
return true;
}
- if (!isClientRemoved) {
- // specified p2p client is not found. already removed.
- return false;
+ private void setWifiP2pInfoOnGroupFormation(InetAddress serverInetAddress) {
+ mWifiP2pInfo.groupFormed = true;
+ mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
+ mWifiP2pInfo.groupOwnerAddress = serverInetAddress;
}
- if (DBG) logd("Modified client list: " + modifiedClientList);
- if (modifiedClientList.length() == 0) {
- modifiedClientList.append("\"\"");
+ private void resetWifiP2pInfo() {
+ mWifiP2pInfo.groupFormed = false;
+ mWifiP2pInfo.isGroupOwner = false;
+ mWifiP2pInfo.groupOwnerAddress = null;
}
- mWifiNative.setNetworkVariable(netId,
- "p2p_client_list", modifiedClientList.toString());
- mWifiNative.saveConfig();
- return true;
- }
- private void setWifiP2pInfoOnGroupFormation(InetAddress serverInetAddress) {
- mWifiP2pInfo.groupFormed = true;
- mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
- mWifiP2pInfo.groupOwnerAddress = serverInetAddress;
- }
-
- private void resetWifiP2pInfo() {
- mWifiP2pInfo.groupFormed = false;
- mWifiP2pInfo.isGroupOwner = false;
- mWifiP2pInfo.groupOwnerAddress = null;
- }
-
- private String getDeviceName(String deviceAddress) {
- WifiP2pDevice d = mPeers.get(deviceAddress);
- if (d != null) {
+ private String getDeviceName(String deviceAddress) {
+ WifiP2pDevice d = mPeers.get(deviceAddress);
+ if (d != null) {
return d.deviceName;
- }
- //Treat the address as name if there is no match
- return deviceAddress;
- }
-
- private String getPersistedDeviceName() {
- String deviceName = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.WIFI_P2P_DEVICE_NAME);
- if (deviceName == null) {
- /* We use the 4 digits of the ANDROID_ID to have a friendly
- * default that has low likelihood of collision with a peer */
- String id = Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.ANDROID_ID);
- return "Android_" + id.substring(0,4);
- }
- return deviceName;
- }
-
- private boolean setAndPersistDeviceName(String devName) {
- if (devName == null) return false;
-
- if (!mWifiNative.setDeviceName(devName)) {
- loge("Failed to set device name " + devName);
- return false;
+ }
+ //Treat the address as name if there is no match
+ return deviceAddress;
}
- mThisDevice.deviceName = devName;
- mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
-
- Settings.Global.putString(mContext.getContentResolver(),
- Settings.Global.WIFI_P2P_DEVICE_NAME, devName);
- sendThisDeviceChangedBroadcast();
- return true;
- }
-
- private boolean setWfdInfo(WifiP2pWfdInfo wfdInfo) {
- boolean success;
-
- if (!wfdInfo.isWfdEnabled()) {
- success = mWifiNative.setWfdEnable(false);
- } else {
- success =
- mWifiNative.setWfdEnable(true)
- && mWifiNative.setWfdDeviceInfo(wfdInfo.getDeviceInfoHex());
+ private String getPersistedDeviceName() {
+ String deviceName = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.WIFI_P2P_DEVICE_NAME);
+ if (deviceName == null) {
+ // We use the 4 digits of the ANDROID_ID to have a friendly
+ // default that has low likelihood of collision with a peer
+ String id = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.ANDROID_ID);
+ return "Android_" + id.substring(0, 4);
+ }
+ return deviceName;
}
- if (!success) {
- loge("Failed to set wfd properties");
- return false;
+ private boolean setAndPersistDeviceName(String devName) {
+ if (devName == null) return false;
+
+ if (!mWifiNative.setDeviceName(devName)) {
+ loge("Failed to set device name " + devName);
+ return false;
+ }
+
+ mThisDevice.deviceName = devName;
+ mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
+
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.WIFI_P2P_DEVICE_NAME, devName);
+ sendThisDeviceChangedBroadcast();
+ return true;
}
- mThisDevice.wfdInfo = wfdInfo;
- sendThisDeviceChangedBroadcast();
- return true;
- }
+ private boolean setWfdInfo(WifiP2pWfdInfo wfdInfo) {
+ boolean success;
- private void initializeP2pSettings() {
- mWifiNative.setPersistentReconnect(true);
- mThisDevice.deviceName = getPersistedDeviceName();
- mWifiNative.setDeviceName(mThisDevice.deviceName);
- // DIRECT-XY-DEVICENAME (XY is randomly generated)
- mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
- mWifiNative.setDeviceType(mThisDevice.primaryDeviceType);
- // Supplicant defaults to using virtual display with display
- // which refers to a remote display. Use physical_display
- mWifiNative.setConfigMethods("virtual_push_button physical_display keypad");
- // STA has higher priority over P2P
- mWifiNative.setConcurrencyPriority("sta");
+ if (!wfdInfo.isWfdEnabled()) {
+ success = mWifiNative.setWfdEnable(false);
+ } else {
+ success =
+ mWifiNative.setWfdEnable(true)
+ && mWifiNative.setWfdDeviceInfo(wfdInfo.getDeviceInfoHex());
+ }
- mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
- updateThisDevice(WifiP2pDevice.AVAILABLE);
- if (DBG) logd("DeviceAddress: " + mThisDevice.deviceAddress);
+ if (!success) {
+ loge("Failed to set wfd properties");
+ return false;
+ }
- mClientInfoList.clear();
- mWifiNative.p2pFlush();
- mWifiNative.p2pServiceFlush();
- mServiceTransactionId = 0;
- mServiceDiscReqId = null;
-
- updatePersistentNetworks(RELOAD);
- }
-
- private void updateThisDevice(int status) {
- mThisDevice.status = status;
- sendThisDeviceChangedBroadcast();
- }
-
- private void handleGroupCreationFailure() {
- resetWifiP2pInfo();
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.FAILED, null, null);
- sendP2pConnectionChangedBroadcast();
-
- // Remove only the peer we failed to connect to so that other devices discovered
- // that have not timed out still remain in list for connection
- boolean peersChanged = mPeers.remove(mPeersLostDuringConnection);
- if (TextUtils.isEmpty(mSavedPeerConfig.deviceAddress) == false &&
- mPeers.remove(mSavedPeerConfig.deviceAddress) != null) {
- peersChanged = true;
- }
- if (peersChanged) {
- sendPeersChangedBroadcast();
+ mThisDevice.wfdInfo = wfdInfo;
+ sendThisDeviceChangedBroadcast();
+ return true;
}
- mPeersLostDuringConnection.clear();
- mServiceDiscReqId = null;
- sendMessage(WifiP2pManager.DISCOVER_PEERS);
- }
+ private void initializeP2pSettings() {
+ mThisDevice.deviceName = getPersistedDeviceName();
+ mWifiNative.setP2pDeviceName(mThisDevice.deviceName);
+ // DIRECT-XY-DEVICENAME (XY is randomly generated)
+ mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);
+ mWifiNative.setP2pDeviceType(mThisDevice.primaryDeviceType);
+ // Supplicant defaults to using virtual display with display
+ // which refers to a remote display. Use physical_display
+ mWifiNative.setConfigMethods("virtual_push_button physical_display keypad");
- private void handleGroupRemoved() {
- if (mGroup.isGroupOwner()) {
- stopDhcpServer(mGroup.getInterface());
- } else {
- if (DBG) logd("stop IpManager");
- stopIpManager();
+ mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
+ updateThisDevice(WifiP2pDevice.AVAILABLE);
+ if (DBG) logd("DeviceAddress: " + mThisDevice.deviceAddress);
+
+ mClientInfoList.clear();
+ mWifiNative.p2pFlush();
+ mWifiNative.p2pServiceFlush();
+ mServiceTransactionId = 0;
+ mServiceDiscReqId = null;
+
+ updatePersistentNetworks(RELOAD);
+ }
+
+ private void updateThisDevice(int status) {
+ mThisDevice.status = status;
+ sendThisDeviceChangedBroadcast();
+ }
+
+ private void handleGroupCreationFailure() {
+ resetWifiP2pInfo();
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.FAILED, null, null);
+ sendP2pConnectionChangedBroadcast();
+
+ // Remove only the peer we failed to connect to so that other devices discovered
+ // that have not timed out still remain in list for connection
+ boolean peersChanged = mPeers.remove(mPeersLostDuringConnection);
+ if (!TextUtils.isEmpty(mSavedPeerConfig.deviceAddress)
+ && mPeers.remove(mSavedPeerConfig.deviceAddress) != null) {
+ peersChanged = true;
+ }
+ if (peersChanged) {
+ sendPeersChangedBroadcast();
+ }
+
+ mPeersLostDuringConnection.clear();
+ mServiceDiscReqId = null;
+ sendMessage(WifiP2pManager.DISCOVER_PEERS);
+ }
+
+ private void handleGroupRemoved() {
+ if (mGroup.isGroupOwner()) {
+ stopDhcpServer(mGroup.getInterface());
+ } else {
+ if (DBG) logd("stop IpManager");
+ stopIpManager();
+ try {
+ mNwService.removeInterfaceFromLocalNetwork(mGroup.getInterface());
+ } catch (RemoteException e) {
+ loge("Failed to remove iface from local network " + e);
+ }
+ }
+
try {
- mNwService.removeInterfaceFromLocalNetwork(mGroup.getInterface());
- } catch (RemoteException e) {
- loge("Failed to remove iface from local network " + e);
+ mNwService.clearInterfaceAddresses(mGroup.getInterface());
+ } catch (Exception e) {
+ loge("Failed to clear addresses " + e);
+ }
+
+ // Clear any timeout that was set. This is essential for devices
+ // that reuse the main p2p interface for a created group.
+ mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
+
+ boolean peersChanged = false;
+ // Remove only peers part of the group, so that other devices discovered
+ // that have not timed out still remain in list for connection
+ for (WifiP2pDevice d : mGroup.getClientList()) {
+ if (mPeers.remove(d)) peersChanged = true;
+ }
+ if (mPeers.remove(mGroup.getOwner())) peersChanged = true;
+ if (mPeers.remove(mPeersLostDuringConnection)) peersChanged = true;
+ if (peersChanged) {
+ sendPeersChangedBroadcast();
+ }
+
+ mGroup = null;
+ mPeersLostDuringConnection.clear();
+ mServiceDiscReqId = null;
+
+ if (mTemporarilyDisconnectedWifi) {
+ if (mWifiChannel != null) {
+ mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 0);
+ } else {
+ loge("handleGroupRemoved(): WifiChannel is null");
+ }
+ mTemporarilyDisconnectedWifi = false;
}
}
- try {
- mNwService.clearInterfaceAddresses(mGroup.getInterface());
- } catch (Exception e) {
- loge("Failed to clear addresses " + e);
+ private void replyToMessage(Message msg, int what) {
+ // State machine initiated requests can have replyTo set to null
+ // indicating there are no recipients, we ignore those reply actions
+ if (msg.replyTo == null) return;
+ Message dstMsg = obtainMessage(msg);
+ dstMsg.what = what;
+ mReplyChannel.replyToMessage(msg, dstMsg);
}
- // Clear any timeout that was set. This is essential for devices
- // that reuse the main p2p interface for a created group.
- mWifiNative.setP2pGroupIdle(mGroup.getInterface(), 0);
-
- boolean peersChanged = false;
- // Remove only peers part of the group, so that other devices discovered
- // that have not timed out still remain in list for connection
- for (WifiP2pDevice d : mGroup.getClientList()) {
- if (mPeers.remove(d)) peersChanged = true;
- }
- if (mPeers.remove(mGroup.getOwner())) peersChanged = true;
- if (mPeers.remove(mPeersLostDuringConnection)) peersChanged = true;
- if (peersChanged) {
- sendPeersChangedBroadcast();
+ private void replyToMessage(Message msg, int what, int arg1) {
+ if (msg.replyTo == null) return;
+ Message dstMsg = obtainMessage(msg);
+ dstMsg.what = what;
+ dstMsg.arg1 = arg1;
+ mReplyChannel.replyToMessage(msg, dstMsg);
}
- mGroup = null;
- mPeersLostDuringConnection.clear();
- mServiceDiscReqId = null;
-
- if (mTemporarilyDisconnectedWifi) {
- mWifiChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST, 0);
- mTemporarilyDisconnectedWifi = false;
+ private void replyToMessage(Message msg, int what, Object obj) {
+ if (msg.replyTo == null) return;
+ Message dstMsg = obtainMessage(msg);
+ dstMsg.what = what;
+ dstMsg.obj = obj;
+ mReplyChannel.replyToMessage(msg, dstMsg);
}
- }
- //State machine initiated requests can have replyTo set to null indicating
- //there are no recipients, we ignore those reply actions
- private void replyToMessage(Message msg, int what) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
+ private Message obtainMessage(Message srcMsg) {
+ // arg2 on the source message has a hash code that needs to
+ // be retained in replies see WifiP2pManager for details
+ Message msg = Message.obtain();
+ msg.arg2 = srcMsg.arg2;
+ return msg;
+ }
- private void replyToMessage(Message msg, int what, int arg1) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- dstMsg.arg1 = arg1;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
+ @Override
+ protected void logd(String s) {
+ Slog.d(TAG, s);
+ }
- private void replyToMessage(Message msg, int what, Object obj) {
- if (msg.replyTo == null) return;
- Message dstMsg = obtainMessage(msg);
- dstMsg.what = what;
- dstMsg.obj = obj;
- mReplyChannel.replyToMessage(msg, dstMsg);
- }
+ @Override
+ protected void loge(String s) {
+ Slog.e(TAG, s);
+ }
- /* arg2 on the source message has a hash code that needs to be retained in replies
- * see WifiP2pManager for details */
- private Message obtainMessage(Message srcMsg) {
- Message msg = Message.obtain();
- msg.arg2 = srcMsg.arg2;
- return msg;
- }
+ /**
+ * Update service discovery request to wpa_supplicant.
+ */
+ private boolean updateSupplicantServiceRequest() {
+ clearSupplicantServiceRequest();
- @Override
- protected void logd(String s) {
- Slog.d(TAG, s);
- }
+ StringBuffer sb = new StringBuffer();
+ for (ClientInfo c: mClientInfoList.values()) {
+ int key;
+ WifiP2pServiceRequest req;
+ for (int i = 0; i < c.mReqList.size(); i++) {
+ req = c.mReqList.valueAt(i);
+ if (req != null) {
+ sb.append(req.getSupplicantQuery());
+ }
+ }
+ }
- @Override
- protected void loge(String s) {
- Slog.e(TAG, s);
- }
+ if (sb.length() == 0) {
+ return false;
+ }
- /**
- * Update service discovery request to wpa_supplicant.
- */
- private boolean updateSupplicantServiceRequest() {
- clearSupplicantServiceRequest();
+ mServiceDiscReqId = mWifiNative.p2pServDiscReq("00:00:00:00:00:00", sb.toString());
+ if (mServiceDiscReqId == null) {
+ return false;
+ }
+ return true;
+ }
- StringBuffer sb = new StringBuffer();
- for (ClientInfo c: mClientInfoList.values()) {
- int key;
- WifiP2pServiceRequest req;
- for (int i=0; i < c.mReqList.size(); i++) {
- req = c.mReqList.valueAt(i);
+ /**
+ * Clear service discovery request in wpa_supplicant
+ */
+ private void clearSupplicantServiceRequest() {
+ if (mServiceDiscReqId == null) return;
+
+ mWifiNative.p2pServDiscCancelReq(mServiceDiscReqId);
+ mServiceDiscReqId = null;
+ }
+
+ private boolean addServiceRequest(Messenger m, WifiP2pServiceRequest req) {
+ if (m == null || req == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ return false;
+ }
+ // TODO: We could track individual service adds separately and avoid
+ // having to do update all service requests on every new request
+ clearClientDeadChannels();
+
+ ClientInfo clientInfo = getClientInfo(m, true);
+ if (clientInfo == null) {
+ return false;
+ }
+
+ ++mServiceTransactionId;
+ //The Wi-Fi p2p spec says transaction id should be non-zero
+ if (mServiceTransactionId == 0) ++mServiceTransactionId;
+ req.setTransactionId(mServiceTransactionId);
+ clientInfo.mReqList.put(mServiceTransactionId, req);
+
+ if (mServiceDiscReqId == null) {
+ return true;
+ }
+
+ return updateSupplicantServiceRequest();
+ }
+
+ private void removeServiceRequest(Messenger m, WifiP2pServiceRequest req) {
+ if (m == null || req == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ }
+
+ ClientInfo clientInfo = getClientInfo(m, false);
+ if (clientInfo == null) {
+ return;
+ }
+
+ // Application does not have transaction id information
+ // go through stored requests to remove
+ boolean removed = false;
+ for (int i = 0; i < clientInfo.mReqList.size(); i++) {
+ if (req.equals(clientInfo.mReqList.valueAt(i))) {
+ removed = true;
+ clientInfo.mReqList.removeAt(i);
+ break;
+ }
+ }
+
+ if (!removed) return;
+
+ if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
+ if (DBG) logd("remove client information from framework");
+ mClientInfoList.remove(clientInfo.mMessenger);
+ }
+
+ if (mServiceDiscReqId == null) {
+ return;
+ }
+
+ updateSupplicantServiceRequest();
+ }
+
+ private void clearServiceRequests(Messenger m) {
+ if (m == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ return;
+ }
+
+ ClientInfo clientInfo = getClientInfo(m, false);
+ if (clientInfo == null) {
+ return;
+ }
+
+ if (clientInfo.mReqList.size() == 0) {
+ return;
+ }
+
+ clientInfo.mReqList.clear();
+
+ if (clientInfo.mServList.size() == 0) {
+ if (DBG) logd("remove channel information from framework");
+ mClientInfoList.remove(clientInfo.mMessenger);
+ }
+
+ if (mServiceDiscReqId == null) {
+ return;
+ }
+
+ updateSupplicantServiceRequest();
+ }
+
+ private boolean addLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
+ if (m == null || servInfo == null) {
+ Log.e(TAG, "Illegal arguments");
+ return false;
+ }
+
+ clearClientDeadChannels();
+
+ ClientInfo clientInfo = getClientInfo(m, true);
+
+ if (clientInfo == null) {
+ return false;
+ }
+
+ if (!clientInfo.mServList.add(servInfo)) {
+ return false;
+ }
+
+ if (!mWifiNative.p2pServiceAdd(servInfo)) {
+ clientInfo.mServList.remove(servInfo);
+ return false;
+ }
+
+ return true;
+ }
+
+ private void removeLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
+ if (m == null || servInfo == null) {
+ Log.e(TAG, "Illegal arguments");
+ return;
+ }
+
+ ClientInfo clientInfo = getClientInfo(m, false);
+ if (clientInfo == null) {
+ return;
+ }
+
+ mWifiNative.p2pServiceDel(servInfo);
+ clientInfo.mServList.remove(servInfo);
+
+ if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
+ if (DBG) logd("remove client information from framework");
+ mClientInfoList.remove(clientInfo.mMessenger);
+ }
+ }
+
+ private void clearLocalServices(Messenger m) {
+ if (m == null) {
+ Log.e(TAG, "Illegal argument(s)");
+ return;
+ }
+
+ ClientInfo clientInfo = getClientInfo(m, false);
+ if (clientInfo == null) {
+ return;
+ }
+
+ for (WifiP2pServiceInfo servInfo: clientInfo.mServList) {
+ mWifiNative.p2pServiceDel(servInfo);
+ }
+
+ clientInfo.mServList.clear();
+ if (clientInfo.mReqList.size() == 0) {
+ if (DBG) logd("remove client information from framework");
+ mClientInfoList.remove(clientInfo.mMessenger);
+ }
+ }
+
+ private void clearClientInfo(Messenger m) {
+ clearLocalServices(m);
+ clearServiceRequests(m);
+ }
+
+ /**
+ * Send the service response to the WifiP2pManager.Channel.
+ * @param WifiP2pServiceResponse response to service discovery
+ */
+ private void sendServiceResponse(WifiP2pServiceResponse resp) {
+ if (resp == null) {
+ Log.e(TAG, "sendServiceResponse with null response");
+ return;
+ }
+ for (ClientInfo c : mClientInfoList.values()) {
+ WifiP2pServiceRequest req = c.mReqList.get(resp.getTransactionId());
if (req != null) {
- sb.append(req.getSupplicantQuery());
+ Message msg = Message.obtain();
+ msg.what = WifiP2pManager.RESPONSE_SERVICE;
+ msg.arg1 = 0;
+ msg.arg2 = 0;
+ msg.obj = resp;
+ if (c.mMessenger == null) {
+ continue;
+ }
+ try {
+ c.mMessenger.send(msg);
+ } catch (RemoteException e) {
+ if (DBG) logd("detect dead channel");
+ clearClientInfo(c.mMessenger);
+ return;
+ }
}
}
}
- if (sb.length() == 0) {
- return false;
- }
+ /**
+ * We don't get notifications of clients that have gone away.
+ * We detect this actively when services are added and throw
+ * them away.
+ *
+ * TODO: This can be done better with full async channels.
+ */
+ private void clearClientDeadChannels() {
+ ArrayList<Messenger> deadClients = new ArrayList<Messenger>();
- mServiceDiscReqId = mWifiNative.p2pServDiscReq("00:00:00:00:00:00", sb.toString());
- if (mServiceDiscReqId == null) {
- return false;
- }
- return true;
- }
-
- /**
- * Clear service discovery request in wpa_supplicant
- */
- private void clearSupplicantServiceRequest() {
- if (mServiceDiscReqId == null) return;
-
- mWifiNative.p2pServDiscCancelReq(mServiceDiscReqId);
- mServiceDiscReqId = null;
- }
-
- /* TODO: We could track individual service adds separately and avoid
- * having to do update all service requests on every new request
- */
- private boolean addServiceRequest(Messenger m, WifiP2pServiceRequest req) {
- clearClientDeadChannels();
- ClientInfo clientInfo = getClientInfo(m, true);
- if (clientInfo == null) {
- return false;
- }
-
- ++mServiceTransactionId;
- //The Wi-Fi p2p spec says transaction id should be non-zero
- if (mServiceTransactionId == 0) ++mServiceTransactionId;
- req.setTransactionId(mServiceTransactionId);
- clientInfo.mReqList.put(mServiceTransactionId, req);
-
- if (mServiceDiscReqId == null) {
- return true;
- }
-
- return updateSupplicantServiceRequest();
- }
-
- private void removeServiceRequest(Messenger m, WifiP2pServiceRequest req) {
- ClientInfo clientInfo = getClientInfo(m, false);
- if (clientInfo == null) {
- return;
- }
-
- //Application does not have transaction id information
- //go through stored requests to remove
- boolean removed = false;
- for (int i=0; i<clientInfo.mReqList.size(); i++) {
- if (req.equals(clientInfo.mReqList.valueAt(i))) {
- removed = true;
- clientInfo.mReqList.removeAt(i);
- break;
- }
- }
-
- if (!removed) return;
-
- if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
- if (DBG) logd("remove client information from framework");
- mClientInfoList.remove(clientInfo.mMessenger);
- }
-
- if (mServiceDiscReqId == null) {
- return;
- }
-
- updateSupplicantServiceRequest();
- }
-
- private void clearServiceRequests(Messenger m) {
-
- ClientInfo clientInfo = getClientInfo(m, false);
- if (clientInfo == null) {
- return;
- }
-
- if (clientInfo.mReqList.size() == 0) {
- return;
- }
-
- clientInfo.mReqList.clear();
-
- if (clientInfo.mServList.size() == 0) {
- if (DBG) logd("remove channel information from framework");
- mClientInfoList.remove(clientInfo.mMessenger);
- }
-
- if (mServiceDiscReqId == null) {
- return;
- }
-
- updateSupplicantServiceRequest();
- }
-
- private boolean addLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
- clearClientDeadChannels();
- ClientInfo clientInfo = getClientInfo(m, true);
- if (clientInfo == null) {
- return false;
- }
-
- if (!clientInfo.mServList.add(servInfo)) {
- return false;
- }
-
- if (!mWifiNative.p2pServiceAdd(servInfo)) {
- clientInfo.mServList.remove(servInfo);
- return false;
- }
-
- return true;
- }
-
- private void removeLocalService(Messenger m, WifiP2pServiceInfo servInfo) {
- ClientInfo clientInfo = getClientInfo(m, false);
- if (clientInfo == null) {
- return;
- }
-
- mWifiNative.p2pServiceDel(servInfo);
-
- clientInfo.mServList.remove(servInfo);
- if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
- if (DBG) logd("remove client information from framework");
- mClientInfoList.remove(clientInfo.mMessenger);
- }
- }
-
- private void clearLocalServices(Messenger m) {
- ClientInfo clientInfo = getClientInfo(m, false);
- if (clientInfo == null) {
- return;
- }
-
- for (WifiP2pServiceInfo servInfo: clientInfo.mServList) {
- mWifiNative.p2pServiceDel(servInfo);
- }
-
- clientInfo.mServList.clear();
- if (clientInfo.mReqList.size() == 0) {
- if (DBG) logd("remove client information from framework");
- mClientInfoList.remove(clientInfo.mMessenger);
- }
- }
-
- private void clearClientInfo(Messenger m) {
- clearLocalServices(m);
- clearServiceRequests(m);
- }
-
- /**
- * Send the service response to the WifiP2pManager.Channel.
- *
- * @param resp
- */
- private void sendServiceResponse(WifiP2pServiceResponse resp) {
- for (ClientInfo c : mClientInfoList.values()) {
- WifiP2pServiceRequest req = c.mReqList.get(resp.getTransactionId());
- if (req != null) {
+ for (ClientInfo c : mClientInfoList.values()) {
Message msg = Message.obtain();
- msg.what = WifiP2pManager.RESPONSE_SERVICE;
+ msg.what = WifiP2pManager.PING;
msg.arg1 = 0;
msg.arg2 = 0;
- msg.obj = resp;
+ msg.obj = null;
+ if (c.mMessenger == null) {
+ continue;
+ }
try {
c.mMessenger.send(msg);
} catch (RemoteException e) {
if (DBG) logd("detect dead channel");
- clearClientInfo(c.mMessenger);
- return;
+ deadClients.add(c.mMessenger);
}
}
- }
- }
- /**
- * We dont get notifications of clients that have gone away.
- * We detect this actively when services are added and throw
- * them away.
- *
- * TODO: This can be done better with full async channels.
- */
- private void clearClientDeadChannels() {
- ArrayList<Messenger> deadClients = new ArrayList<Messenger>();
-
- for (ClientInfo c : mClientInfoList.values()) {
- Message msg = Message.obtain();
- msg.what = WifiP2pManager.PING;
- msg.arg1 = 0;
- msg.arg2 = 0;
- msg.obj = null;
- try {
- c.mMessenger.send(msg);
- } catch (RemoteException e) {
- if (DBG) logd("detect dead channel");
- deadClients.add(c.mMessenger);
+ for (Messenger m : deadClients) {
+ clearClientInfo(m);
}
}
- for (Messenger m : deadClients) {
- clearClientInfo(m);
- }
- }
+ /**
+ * Return the specified ClientInfo.
+ * @param m Messenger
+ * @param createIfNotExist if true and the specified channel info does not exist,
+ * create new client info.
+ * @return the specified ClientInfo.
+ */
+ private ClientInfo getClientInfo(Messenger m, boolean createIfNotExist) {
+ ClientInfo clientInfo = mClientInfoList.get(m);
- /**
- * Return the specified ClientInfo.
- * @param m Messenger
- * @param createIfNotExist if true and the specified channel info does not exist,
- * create new client info.
- * @return the specified ClientInfo.
- */
- private ClientInfo getClientInfo(Messenger m, boolean createIfNotExist) {
- ClientInfo clientInfo = mClientInfoList.get(m);
+ if (clientInfo == null && createIfNotExist) {
+ if (DBG) logd("add a new client");
+ clientInfo = new ClientInfo(m);
+ mClientInfoList.put(m, clientInfo);
+ }
- if (clientInfo == null && createIfNotExist) {
- if (DBG) logd("add a new client");
- clientInfo = new ClientInfo(m);
- mClientInfoList.put(m, clientInfo);
+ return clientInfo;
}
- return clientInfo;
- }
-
+ /**
+ * Enforces permissions on the caller who is requesting for P2p Peers
+ * @param pkg Bundle containing the calling package string
+ * @param uid of the caller
+ * @return WifiP2pDeviceList the peer list
+ */
+ private WifiP2pDeviceList getPeers(Bundle pkg, int uid) {
+ String pkgName = pkg.getString(WifiP2pManager.CALLING_PACKAGE);
+ boolean scanPermission = false;
+ WifiPermissionsUtil wifiPermissionsUtil;
+ // getPeers() is guaranteed to be invoked after Wifi Service is up
+ // This ensures getInstance() will return a non-null object now
+ if (mWifiInjector == null) {
+ mWifiInjector = WifiInjector.getInstance();
+ }
+ wifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil();
+ // Minimum Version to enforce location permission is O or later
+ try {
+ scanPermission = wifiPermissionsUtil.canAccessScanResults(pkgName, uid,
+ Build.VERSION_CODES.O);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Security Exception, cannot access peer list");
+ }
+ if (scanPermission) {
+ return new WifiP2pDeviceList(mPeers);
+ } else {
+ return new WifiP2pDeviceList();
+ }
+ }
}
/**
@@ -3215,20 +3365,14 @@
*/
private class ClientInfo {
- /*
- * A reference to WifiP2pManager.Channel handler.
- * The response of this request is notified to WifiP2pManager.Channel handler
- */
+ // A reference to WifiP2pManager.Channel handler.
+ // The response of this request is notified to WifiP2pManager.Channel handler
private Messenger mMessenger;
- /*
- * A service discovery request list.
- */
+ // A service discovery request list.
private SparseArray<WifiP2pServiceRequest> mReqList;
- /*
- * A local service information list.
- */
+ // A local service information list.
private List<WifiP2pServiceInfo> mServList;
private ClientInfo(Messenger m) {
diff --git a/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java b/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java
index 0f92431..01d64e9 100644
--- a/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java
+++ b/service/java/com/android/server/wifi/scanner/BackgroundScanScheduler.java
@@ -36,7 +36,6 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@@ -441,7 +440,6 @@
schedule.max_ap_per_scan = 0;
schedule.report_threshold_num_scans = getMaxBatch();
- HashSet<Integer> hiddenNetworkIdSet = new HashSet<>();
// set all buckets in schedule
int bucketId = 0;
@@ -458,12 +456,6 @@
&& settings.maxScansToCache < schedule.report_threshold_num_scans) {
schedule.report_threshold_num_scans = settings.maxScansToCache;
}
- // note hidden networks
- if (settings.hiddenNetworkIds != null) {
- for (int j = 0; j < settings.hiddenNetworkIds.length; j++) {
- hiddenNetworkIdSet.add(settings.hiddenNetworkIds[j]);
- }
- }
}
bucketId++;
}
@@ -473,13 +465,6 @@
if (schedule.max_ap_per_scan == 0 || schedule.max_ap_per_scan > getMaxApPerScan()) {
schedule.max_ap_per_scan = getMaxApPerScan();
}
- if (hiddenNetworkIdSet.size() > 0) {
- schedule.hiddenNetworkIds = new int[hiddenNetworkIdSet.size()];
- int numHiddenNetworks = 0;
- for (Integer hiddenNetworkId : hiddenNetworkIdSet) {
- schedule.hiddenNetworkIds[numHiddenNetworks++] = hiddenNetworkId;
- }
- }
// update base period as gcd of periods
if (schedule.num_buckets > 0) {
@@ -569,7 +554,6 @@
ScanSettings settings = new ScanSettings();
settings.band = originalSettings.band;
settings.channels = originalSettings.channels;
- settings.hiddenNetworkIds = originalSettings.hiddenNetworkIds;
settings.periodInMs = originalSettings.periodInMs;
settings.reportEvents = originalSettings.reportEvents;
settings.numBssidsPerScan = originalSettings.numBssidsPerScan;
diff --git a/service/java/com/android/server/wifi/scanner/ChannelHelper.java b/service/java/com/android/server/wifi/scanner/ChannelHelper.java
index acb0ac8..d87df07 100644
--- a/service/java/com/android/server/wifi/scanner/ChannelHelper.java
+++ b/service/java/com/android/server/wifi/scanner/ChannelHelper.java
@@ -222,10 +222,10 @@
public abstract void fillBucketSettings(WifiNative.BucketSettings bucket, int maxChannels);
/**
- * Gets the list of channels that should be supplied to supplicant for a scan. Will either
- * be a collection of all channels or null if all channels should be scanned.
+ * Gets the list of channels scan. Will either be a collection of all channels or null
+ * if all channels should be scanned.
*/
- public abstract Set<Integer> getSupplicantScanFreqs();
+ public abstract Set<Integer> getScanFreqs();
}
diff --git a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java
index d85c818..211e62a 100644
--- a/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/HalWifiScannerImpl.java
@@ -24,11 +24,12 @@
import android.util.Log;
import com.android.server.wifi.Clock;
+import com.android.server.wifi.WifiMonitor;
import com.android.server.wifi.WifiNative;
/**
* WifiScanner implementation that takes advantage of the gscan HAL API
- * The gscan API is used to perform background scans and wpa_supplicant is used for onehot scans.
+ * The gscan API is used to perform background scans and wificond is used for oneshot scans.
* @see com.android.server.wifi.scanner.WifiScannerImpl for more details on each method.
*/
public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callback {
@@ -37,14 +38,16 @@
private final WifiNative mWifiNative;
private final ChannelHelper mChannelHelper;
- private final SupplicantWifiScannerImpl mSupplicantScannerDelegate;
+ private final WificondScannerImpl mWificondScannerDelegate;
private final boolean mHalBasedPnoSupported;
- public HalWifiScannerImpl(Context context, WifiNative wifiNative, Looper looper, Clock clock) {
+ public HalWifiScannerImpl(Context context, WifiNative wifiNative, WifiMonitor wifiMonitor,
+ Looper looper, Clock clock) {
mWifiNative = wifiNative;
mChannelHelper = new HalChannelHelper(wifiNative);
- mSupplicantScannerDelegate =
- new SupplicantWifiScannerImpl(context, wifiNative, mChannelHelper, looper, clock);
+ mWificondScannerDelegate =
+ new WificondScannerImpl(context, wifiNative, wifiMonitor, mChannelHelper,
+ looper, clock);
// We are not going to support HAL ePNO currently.
mHalBasedPnoSupported = false;
@@ -58,12 +61,12 @@
@Override
public void cleanup() {
- mSupplicantScannerDelegate.cleanup();
+ mWificondScannerDelegate.cleanup();
}
@Override
public boolean getScanCapabilities(WifiNative.ScanCapabilities capabilities) {
- return mWifiNative.getScanCapabilities(capabilities);
+ return mWifiNative.getBgScanCapabilities(capabilities);
}
@Override
@@ -73,12 +76,12 @@
public boolean startSingleScan(WifiNative.ScanSettings settings,
WifiNative.ScanEventHandler eventHandler) {
- return mSupplicantScannerDelegate.startSingleScan(settings, eventHandler);
+ return mWificondScannerDelegate.startSingleScan(settings, eventHandler);
}
@Override
public WifiScanner.ScanData getLatestSingleScanResults() {
- return mSupplicantScannerDelegate.getLatestSingleScanResults();
+ return mWificondScannerDelegate.getLatestSingleScanResults();
}
@Override
@@ -89,27 +92,27 @@
+ ",eventHandler=" + eventHandler);
return false;
}
- return mWifiNative.startScan(settings, eventHandler);
+ return mWifiNative.startBgScan(settings, eventHandler);
}
@Override
public void stopBatchedScan() {
- mWifiNative.stopScan();
+ mWifiNative.stopBgScan();
}
@Override
public void pauseBatchedScan() {
- mWifiNative.pauseScan();
+ mWifiNative.pauseBgScan();
}
@Override
public void restartBatchedScan() {
- mWifiNative.restartScan();
+ mWifiNative.restartBgScan();
}
@Override
public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) {
- return mWifiNative.getScanResults(flush);
+ return mWifiNative.getBgScanResults();
}
@Override
@@ -118,7 +121,7 @@
if (mHalBasedPnoSupported) {
return mWifiNative.setPnoList(settings, eventHandler);
} else {
- return mSupplicantScannerDelegate.setHwPnoList(settings, eventHandler);
+ return mWificondScannerDelegate.setHwPnoList(settings, eventHandler);
}
}
@@ -127,7 +130,7 @@
if (mHalBasedPnoSupported) {
return mWifiNative.resetPnoList();
} else {
- return mSupplicantScannerDelegate.resetHwPnoList();
+ return mWificondScannerDelegate.resetHwPnoList();
}
}
@@ -136,7 +139,7 @@
if (mHalBasedPnoSupported) {
return true;
} else {
- return mSupplicantScannerDelegate.isHwPnoSupported(isConnectedPno);
+ return mWificondScannerDelegate.isHwPnoSupported(isConnectedPno);
}
}
@@ -145,29 +148,7 @@
if (mHalBasedPnoSupported) {
return true;
} else {
- return mSupplicantScannerDelegate.shouldScheduleBackgroundScanForHwPno();
+ return mWificondScannerDelegate.shouldScheduleBackgroundScanForHwPno();
}
}
-
- @Override
- public boolean setHotlist(WifiScanner.HotlistSettings settings,
- WifiNative.HotlistEventHandler eventHandler) {
- return mWifiNative.setHotlist(settings, eventHandler);
- }
-
- @Override
- public void resetHotlist() {
- mWifiNative.resetHotlist();
- }
-
- @Override
- public boolean trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings,
- WifiNative.SignificantWifiChangeEventHandler handler) {
- return mWifiNative.trackSignificantWifiChange(settings, handler);
- }
-
- @Override
- public void untrackSignificantWifiChange() {
- mWifiNative.untrackSignificantWifiChange();
- }
}
diff --git a/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java b/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java
index acddc26..33cce1c 100644
--- a/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java
+++ b/service/java/com/android/server/wifi/scanner/KnownBandsChannelHelper.java
@@ -263,7 +263,7 @@
}
@Override
- public Set<Integer> getSupplicantScanFreqs() {
+ public Set<Integer> getScanFreqs() {
if (mExactBands == WifiScanner.WIFI_BAND_BOTH_WITH_DFS) {
return null;
} else {
diff --git a/service/java/com/android/server/wifi/scanner/NoBandChannelHelper.java b/service/java/com/android/server/wifi/scanner/NoBandChannelHelper.java
index 4f8373b..b2eeada 100644
--- a/service/java/com/android/server/wifi/scanner/NoBandChannelHelper.java
+++ b/service/java/com/android/server/wifi/scanner/NoBandChannelHelper.java
@@ -100,7 +100,7 @@
@Override
public boolean partiallyContainsBand(int band) {
- // We don't need to partially collapse settings in supplicant scanner because we
+ // We don't need to partially collapse settings in wificond scanner because we
// don't have any limitation on the number of channels that can be scanned. We also
// don't currently keep track of bands very well in NoBandChannelHelper.
return false;
@@ -124,7 +124,7 @@
@Override
public Set<Integer> getMissingChannelsFromBand(int band) {
- // We don't need to partially collapse settings in supplicant scanner because we
+ // We don't need to partially collapse settings in wificond scanner because we
// don't have any limitation on the number of channels that can be scanned. We also
// don't currently keep track of bands very well in NoBandChannelHelper.
return new ArraySet<Integer>();
@@ -132,7 +132,7 @@
@Override
public Set<Integer> getContainingChannelsFromBand(int band) {
- // We don't need to partially collapse settings in supplicant scanner because we
+ // We don't need to partially collapse settings in wificond scanner because we
// don't have any limitation on the number of channels that can be scanned. We also
// don't currently keep track of bands very well in NoBandChannelHelper.
return new ArraySet<Integer>();
@@ -166,7 +166,7 @@
}
@Override
- public Set<Integer> getSupplicantScanFreqs() {
+ public Set<Integer> getScanFreqs() {
if (mAllChannels) {
return null;
} else {
diff --git a/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
index 9012fc6..e0fb535 100644
--- a/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WifiScannerImpl.java
@@ -22,6 +22,8 @@
import android.os.Looper;
import com.android.server.wifi.Clock;
+import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.WifiMonitor;
import com.android.server.wifi.WifiNative;
import java.util.Comparator;
@@ -44,11 +46,13 @@
*/
public static final WifiScannerImplFactory DEFAULT_FACTORY = new WifiScannerImplFactory() {
public WifiScannerImpl create(Context context, Looper looper, Clock clock) {
- WifiNative wifiNative = WifiNative.getWlanNativeInterface();
- if (wifiNative.getScanCapabilities(new WifiNative.ScanCapabilities())) {
- return new HalWifiScannerImpl(context, wifiNative, looper, clock);
+ WifiNative wifiNative = WifiInjector.getInstance().getWifiNative();
+ WifiMonitor wifiMonitor = WifiInjector.getInstance().getWifiMonitor();
+ if (wifiNative.getBgScanCapabilities(new WifiNative.ScanCapabilities())) {
+ return new HalWifiScannerImpl(context, wifiNative, wifiMonitor, looper, clock);
} else {
- return new SupplicantWifiScannerImpl(context, wifiNative, looper, clock);
+ return new WificondScannerImpl(context, wifiNative, wifiMonitor, looper,
+ clock);
}
}
};
@@ -154,26 +158,4 @@
* @return true if background scan needs to be started, false otherwise.
*/
public abstract boolean shouldScheduleBackgroundScanForHwPno();
-
- /**
- * Set a new hotlist
- */
- public abstract boolean setHotlist(WifiScanner.HotlistSettings settings,
- WifiNative.HotlistEventHandler eventHandler);
-
- /**
- * Reset any active hotlist
- */
- public abstract void resetHotlist();
-
- /**
- * Start tracking significant wifi changes
- */
- public abstract boolean trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings,
- WifiNative.SignificantWifiChangeEventHandler handler);
-
- /**
- * Stop tracking significant wifi changes
- */
- public abstract void untrackSignificantWifiChange();
}
diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
index 9f8fb2f..af874b9 100644
--- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java
@@ -18,7 +18,6 @@
import android.Manifest;
import android.app.AlarmManager;
-import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -28,45 +27,48 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
-import android.net.wifi.WifiScanner.BssidInfo;
import android.net.wifi.WifiScanner.ChannelSpec;
import android.net.wifi.WifiScanner.PnoSettings;
import android.net.wifi.WifiScanner.ScanData;
import android.net.wifi.WifiScanner.ScanSettings;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.WorkSource;
import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.wifi.Clock;
+import com.android.server.wifi.FrameworkFacade;
import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.WifiLog;
import com.android.server.wifi.WifiMetrics;
-import com.android.server.wifi.WifiMetricsProto;
import com.android.server.wifi.WifiNative;
import com.android.server.wifi.WifiStateMachine;
+import com.android.server.wifi.nano.WifiMetricsProto;
import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection;
+import com.android.server.wifi.util.WifiHandler;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
-import java.util.Set;
+import java.util.List;
public class WifiScanningServiceImpl extends IWifiScanner.Stub {
@@ -78,6 +80,8 @@
private final LocalLog mLocalLog = new LocalLog(512);
+ private WifiLog mLog;
+
private void localLog(String message) {
mLocalLog.log(message);
}
@@ -97,11 +101,11 @@
@Override
public Messenger getMessenger() {
if (mClientHandler != null) {
+ mLog.trace("getMessenger() uid=%").c(Binder.getCallingUid()).flush();
return new Messenger(mClientHandler);
- } else {
- loge("WifiScanningServiceImpl trying to get messenger w/o initialization");
- return null;
}
+ loge("WifiScanningServiceImpl trying to get messenger w/o initialization");
+ return null;
}
@Override
@@ -114,6 +118,7 @@
}
Bundle b = new Bundle();
b.putIntegerArrayList(WifiScanner.GET_AVAILABLE_CHANNELS_EXTRA, list);
+ mLog.trace("getAvailableChannels uid=%").c(Binder.getCallingUid()).flush();
return b;
}
@@ -124,14 +129,15 @@
"LocationHardware");
}
- private class ClientHandler extends Handler {
+ private class ClientHandler extends WifiHandler {
- ClientHandler(Looper looper) {
- super(looper);
+ ClientHandler(String tag, Looper looper) {
+ super(tag, looper);
}
@Override
public void handleMessage(Message msg) {
+ super.handleMessage(msg);
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
ExternalClientInfo client = (ExternalClientInfo) mClients.get(msg.replyTo);
@@ -143,7 +149,7 @@
return;
}
- AsyncChannel ac = new AsyncChannel();
+ AsyncChannel ac = mFrameworkFacade.makeWifiAsyncChannel(TAG);
ac.connected(mContext, this, msg.replyTo);
client = new ExternalClientInfo(msg.sendingUid, msg.replyTo, ac);
@@ -151,7 +157,6 @@
ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
AsyncChannel.STATUS_SUCCESSFUL);
-
localLog("client connected: " + client);
return;
}
@@ -182,16 +187,22 @@
return;
}
- // Since this message is sent from WifiScanner using |sendMessageSynchronously| which
- // doesn't set the correct |msg.replyTo| field.
+ // Since the CMD_GET_SCAN_RESULTS and CMD_GET_SINGLE_SCAN_RESULTS messages are
+ // sent from WifiScanner using |sendMessageSynchronously|, handle separately since
+ // the |msg.replyTo| field does not actually correspond to the Messenger that is
+ // registered for that client.
if (msg.what == WifiScanner.CMD_GET_SCAN_RESULTS) {
mBackgroundScanStateMachine.sendMessage(Message.obtain(msg));
return;
}
+ if (msg.what == WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS) {
+ mSingleScanStateMachine.sendMessage(Message.obtain(msg));
+ return;
+ }
ClientInfo ci = mClients.get(msg.replyTo);
if (ci == null) {
- loge("Could not find client info for message " + msg.replyTo);
+ loge("Could not find client info for message " + msg.replyTo + ", msg=" + msg);
replyFailed(msg, WifiScanner.REASON_INVALID_LISTENER, "Could not find listener");
return;
}
@@ -199,8 +210,6 @@
switch (msg.what) {
case WifiScanner.CMD_START_BACKGROUND_SCAN:
case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
- case WifiScanner.CMD_SET_HOTLIST:
- case WifiScanner.CMD_RESET_HOTLIST:
mBackgroundScanStateMachine.sendMessage(Message.obtain(msg));
break;
case WifiScanner.CMD_START_PNO_SCAN:
@@ -211,11 +220,6 @@
case WifiScanner.CMD_STOP_SINGLE_SCAN:
mSingleScanStateMachine.sendMessage(Message.obtain(msg));
break;
- case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
- case WifiScanner.CMD_START_TRACKING_CHANGE:
- case WifiScanner.CMD_STOP_TRACKING_CHANGE:
- mWifiChangeStateMachine.sendMessage(Message.obtain(msg));
- break;
case WifiScanner.CMD_REGISTER_SCAN_LISTENER:
logScanRequest("registerScanListener", ci, msg.arg2, null, null, null);
mSingleScanListeners.addRequest(ci, msg.arg2, null, null);
@@ -261,13 +265,13 @@
private WifiBackgroundScanStateMachine mBackgroundScanStateMachine;
private WifiSingleScanStateMachine mSingleScanStateMachine;
- private WifiChangeStateMachine mWifiChangeStateMachine;
private WifiPnoScanStateMachine mPnoScanStateMachine;
private ClientHandler mClientHandler;
private final IBatteryStats mBatteryStats;
private final AlarmManager mAlarmManager;
private final WifiMetrics mWifiMetrics;
private final Clock mClock;
+ private final FrameworkFacade mFrameworkFacade;
WifiScanningServiceImpl(Context context, Looper looper,
WifiScannerImpl.WifiScannerImplFactory scannerImplFactory, IBatteryStats batteryStats,
@@ -280,14 +284,14 @@
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mWifiMetrics = wifiInjector.getWifiMetrics();
mClock = wifiInjector.getClock();
-
+ mLog = wifiInjector.makeLog(TAG);
+ mFrameworkFacade = wifiInjector.getFrameworkFacade();
mPreviousSchedule = null;
}
public void startService() {
- mClientHandler = new ClientHandler(mLooper);
+ mClientHandler = new ClientHandler(TAG, mLooper);
mBackgroundScanStateMachine = new WifiBackgroundScanStateMachine(mLooper);
- mWifiChangeStateMachine = new WifiChangeStateMachine(mLooper);
mSingleScanStateMachine = new WifiSingleScanStateMachine(mLooper);
mPnoScanStateMachine = new WifiPnoScanStateMachine(mLooper);
@@ -311,11 +315,19 @@
}, new IntentFilter(WifiManager.WIFI_SCAN_AVAILABLE));
mBackgroundScanStateMachine.start();
- mWifiChangeStateMachine.start();
mSingleScanStateMachine.start();
mPnoScanStateMachine.start();
}
+ /**
+ * Provide a way for unit tests to set valid log object in the WifiHandler
+ * @param log WifiLog object to assign to the clientHandler
+ */
+ @VisibleForTesting
+ public void setWifiHandlerLogForTest(WifiLog log) {
+ mClientHandler.setWifiLog(log);
+ }
+
private static boolean isWorkSourceValid(WorkSource workSource) {
return workSource != null && workSource.size() > 0 && workSource.get(0) >= 0;
}
@@ -426,6 +438,15 @@
* executed after transitioning back to IdleState.
*/
class WifiSingleScanStateMachine extends StateMachine implements WifiNative.ScanEventHandler {
+ /**
+ * Maximum age of results that we return from our cache via
+ * {@link WifiScanner#getScanResults()}.
+ * This is currently set to 3 minutes to restore parity with the wpa_supplicant's scan
+ * result cache expiration policy. (See b/62253332 for details)
+ */
+ @VisibleForTesting
+ public static final int CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS = 180 * 1000;
+
private final DefaultState mDefaultState = new DefaultState();
private final DriverStartedState mDriverStartedState = new DriverStartedState();
private final IdleState mIdleState = new IdleState();
@@ -435,6 +456,9 @@
private RequestList<ScanSettings> mActiveScans = new RequestList<>();
private RequestList<ScanSettings> mPendingScans = new RequestList<>();
+ // Scan results cached from the last full single scan request.
+ private final List<ScanResult> mCachedScanResults = new ArrayList<>();
+
WifiSingleScanStateMachine(Looper looper) {
super("WifiSingleScanStateMachine", looper);
@@ -519,10 +543,31 @@
case CMD_FULL_SCAN_RESULTS:
if (DBG) localLog("ignored full scan result event");
return HANDLED;
+ case WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS:
+ msg.obj = new WifiScanner.ParcelableScanResults(
+ filterCachedScanResultsByAge());
+ replySucceeded(msg);
+ return HANDLED;
default:
return NOT_HANDLED;
}
+ }
+ /**
+ * Filter out any scan results that are older than
+ * {@link #CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS}.
+ *
+ * @return Filtered list of scan results.
+ */
+ private ScanResult[] filterCachedScanResultsByAge() {
+ // Using ScanResult.timestamp here to ensure that we use the same fields as
+ // WificondScannerImpl for filtering stale results.
+ long currentTimeInMillis = mClock.getElapsedSinceBootMillis();
+ return mCachedScanResults.stream()
+ .filter(scanResult
+ -> ((currentTimeInMillis - (scanResult.timestamp / 1000))
+ < CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS))
+ .toArray(ScanResult[]::new);
}
}
@@ -534,6 +579,9 @@
class DriverStartedState extends State {
@Override
public void exit() {
+ // clear scan results when scan mode is not active
+ mCachedScanResults.clear();
+
mWifiMetrics.incrementScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED,
mPendingScans.size());
@@ -655,6 +703,7 @@
case CMD_SCAN_FAILED:
mWifiMetrics.incrementScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_UNKNOWN, mActiveScans.size());
+ sendScanResultBroadcast(false);
sendOpFailedToAllAndClear(mActiveScans, WifiScanner.REASON_UNSPECIFIED,
"Scan failed");
transitionTo(mIdleState);
@@ -702,16 +751,18 @@
return false;
}
- if (settings.hiddenNetworkIds != null) {
- if (mActiveScanSettings.hiddenNetworkIds == null) {
+ if (!ArrayUtils.isEmpty(settings.hiddenNetworks)) {
+ if (ArrayUtils.isEmpty(mActiveScanSettings.hiddenNetworks)) {
return false;
}
- Set<Integer> activeHiddenNetworkIds = new HashSet<>();
- for (int id : mActiveScanSettings.hiddenNetworkIds) {
- activeHiddenNetworkIds.add(id);
+ List<WifiNative.HiddenNetwork> activeHiddenNetworks = new ArrayList<>();
+ for (WifiNative.HiddenNetwork hiddenNetwork : mActiveScanSettings.hiddenNetworks) {
+ activeHiddenNetworks.add(hiddenNetwork);
}
- for (int id : settings.hiddenNetworkIds) {
- if (!activeHiddenNetworkIds.contains(id)) {
+ for (ScanSettings.HiddenNetwork hiddenNetwork : settings.hiddenNetworks) {
+ WifiNative.HiddenNetwork nativeHiddenNetwork = new WifiNative.HiddenNetwork();
+ nativeHiddenNetwork.ssid = hiddenNetwork.ssid;
+ if (!activeHiddenNetworks.contains(nativeHiddenNetwork)) {
return false;
}
}
@@ -750,12 +801,14 @@
bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
ChannelCollection channels = mChannelHelper.createChannelCollection();
- HashSet<Integer> hiddenNetworkIdSet = new HashSet<>();
+ List<WifiNative.HiddenNetwork> hiddenNetworkList = new ArrayList<>();
for (RequestInfo<ScanSettings> entry : mPendingScans) {
channels.addChannels(entry.settings);
- if (entry.settings.hiddenNetworkIds != null) {
- for (int i = 0; i < entry.settings.hiddenNetworkIds.length; i++) {
- hiddenNetworkIdSet.add(entry.settings.hiddenNetworkIds[i]);
+ if (entry.settings.hiddenNetworks != null) {
+ for (int i = 0; i < entry.settings.hiddenNetworks.length; i++) {
+ WifiNative.HiddenNetwork hiddenNetwork = new WifiNative.HiddenNetwork();
+ hiddenNetwork.ssid = entry.settings.hiddenNetworks[i].ssid;
+ hiddenNetworkList.add(hiddenNetwork);
}
}
if ((entry.settings.reportEvents & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT)
@@ -763,11 +816,11 @@
bucketSettings.report_events |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT;
}
}
- if (hiddenNetworkIdSet.size() > 0) {
- settings.hiddenNetworkIds = new int[hiddenNetworkIdSet.size()];
+ if (hiddenNetworkList.size() > 0) {
+ settings.hiddenNetworks = new WifiNative.HiddenNetwork[hiddenNetworkList.size()];
int numHiddenNetworks = 0;
- for (Integer hiddenNetworkId : hiddenNetworkIdSet) {
- settings.hiddenNetworkIds[numHiddenNetworks++] = hiddenNetworkId;
+ for (WifiNative.HiddenNetwork hiddenNetwork : hiddenNetworkList) {
+ settings.hiddenNetworks[numHiddenNetworks++] = hiddenNetwork;
}
}
@@ -817,6 +870,13 @@
}
}
+ private void sendScanResultBroadcast(boolean scanSucceeded) {
+ Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, scanSucceeded);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
void reportScanResults(ScanData results) {
if (results != null && results.getResults() != null) {
if (results.getResults().length > 0) {
@@ -845,19 +905,27 @@
describeForLog(allResults));
entry.reportEvent(WifiScanner.CMD_SCAN_RESULT, 0, parcelableAllResults);
}
+
+ if (results.isAllChannelsScanned()) {
+ mCachedScanResults.clear();
+ mCachedScanResults.addAll(Arrays.asList(results.getResults()));
+ sendScanResultBroadcast(true);
+ }
+ }
+
+ List<ScanResult> getCachedScanResultsAsList() {
+ return mCachedScanResults;
}
}
class WifiBackgroundScanStateMachine extends StateMachine
- implements WifiNative.ScanEventHandler, WifiNative.HotlistEventHandler {
+ implements WifiNative.ScanEventHandler {
private final DefaultState mDefaultState = new DefaultState();
private final StartedState mStartedState = new StartedState();
private final PausedState mPausedState = new PausedState();
private final RequestList<ScanSettings> mActiveBackgroundScans = new RequestList<>();
- private final RequestList<WifiScanner.HotlistSettings> mActiveHotlistSettings =
- new RequestList<>();
WifiBackgroundScanStateMachine(Looper looper) {
super("WifiBackgroundScanStateMachine", looper);
@@ -883,11 +951,6 @@
updateSchedule();
}
- public void removeHotlistSettings(ClientInfo ci) {
- mActiveHotlistSettings.removeAllForClient(ci);
- resetHotlist();
- }
-
@Override
public void onScanStatus(int event) {
if (DBG) localLog("onScanStatus event received, event=" + event);
@@ -924,24 +987,11 @@
sendMessage(CMD_SCAN_RESTARTED);
}
- @Override
- public void onHotlistApFound(ScanResult[] results) {
- if (DBG) localLog("onHotlistApFound event received");
- sendMessage(CMD_HOTLIST_AP_FOUND, 0, 0, results);
- }
-
- @Override
- public void onHotlistApLost(ScanResult[] results) {
- if (DBG) localLog("onHotlistApLost event received");
- sendMessage(CMD_HOTLIST_AP_LOST, 0, 0, results);
- }
-
class DefaultState extends State {
@Override
public void enter() {
if (DBG) localLog("DefaultState");
mActiveBackgroundScans.clear();
- mActiveHotlistSettings.clear();
}
@Override
@@ -964,6 +1014,11 @@
loge("could not get scan capabilities");
return HANDLED;
}
+ if (capabilities.max_scan_buckets <= 0) {
+ loge("invalid max buckets in scan capabilities "
+ + capabilities.max_scan_buckets);
+ return HANDLED;
+ }
mBackgroundScheduler.setMaxBuckets(capabilities.max_scan_buckets);
mBackgroundScheduler.setMaxApPerScan(capabilities.max_ap_cache_per_scan);
@@ -980,8 +1035,6 @@
case WifiScanner.CMD_STOP_BACKGROUND_SCAN:
case WifiScanner.CMD_START_SINGLE_SCAN:
case WifiScanner.CMD_STOP_SINGLE_SCAN:
- case WifiScanner.CMD_SET_HOTLIST:
- case WifiScanner.CMD_RESET_HOTLIST:
case WifiScanner.CMD_GET_SCAN_RESULTS:
replyFailed(msg, WifiScanner.REASON_UNSPECIFIED, "not available");
break;
@@ -1013,8 +1066,6 @@
public void exit() {
sendBackgroundScanFailedToAllAndClear(
WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted");
- sendHotlistFailedToAllAndClear(
- WifiScanner.REASON_UNSPECIFIED, "Scan was interrupted");
mScannerImpl.cleanup();
}
@@ -1053,28 +1104,12 @@
reportScanResults(mScannerImpl.getLatestBatchedScanResults(true));
replySucceeded(msg);
break;
- case WifiScanner.CMD_SET_HOTLIST:
- if (addHotlist(ci, msg.arg2, (WifiScanner.HotlistSettings) msg.obj)) {
- replySucceeded(msg);
- } else {
- replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
- }
- break;
- case WifiScanner.CMD_RESET_HOTLIST:
- removeHotlist(ci, msg.arg2);
- break;
case CMD_SCAN_RESULTS_AVAILABLE:
reportScanResults(mScannerImpl.getLatestBatchedScanResults(true));
break;
case CMD_FULL_SCAN_RESULTS:
reportFullScanResult((ScanResult) msg.obj, /* bucketsScanned */ msg.arg2);
break;
- case CMD_HOTLIST_AP_FOUND:
- reportHotlistResults(WifiScanner.CMD_AP_FOUND, (ScanResult[]) msg.obj);
- break;
- case CMD_HOTLIST_AP_LOST:
- reportHotlistResults(WifiScanner.CMD_AP_LOST, (ScanResult[]) msg.obj);
- break;
case CMD_SCAN_PAUSED:
reportScanResults((ScanData[]) msg.obj);
transitionTo(mPausedState);
@@ -1260,6 +1295,10 @@
}
private void reportScanResults(ScanData[] results) {
+ if (results == null) {
+ Log.d(TAG,"The results is null, nothing to report.");
+ return;
+ }
for (ScanData result : results) {
if (result != null && result.getResults() != null) {
if (result.getResults().length > 0) {
@@ -1294,106 +1333,6 @@
}
mActiveBackgroundScans.clear();
}
-
- private boolean addHotlist(ClientInfo ci, int handler,
- WifiScanner.HotlistSettings settings) {
- if (ci == null) {
- Log.d(TAG, "Failing hotlist request ClientInfo not found " + handler);
- return false;
- }
- mActiveHotlistSettings.addRequest(ci, handler, null, settings);
- resetHotlist();
- return true;
- }
-
- private void removeHotlist(ClientInfo ci, int handler) {
- if (ci != null) {
- mActiveHotlistSettings.removeRequest(ci, handler);
- resetHotlist();
- }
- }
-
- private void resetHotlist() {
- if (mScannerImpl == null) {
- loge("Failed to update hotlist because WifiScanningService is not initialized");
- return;
- }
-
- Collection<WifiScanner.HotlistSettings> settings =
- mActiveHotlistSettings.getAllSettings();
- int num_hotlist_ap = 0;
-
- for (WifiScanner.HotlistSettings s : settings) {
- num_hotlist_ap += s.bssidInfos.length;
- }
-
- if (num_hotlist_ap == 0) {
- mScannerImpl.resetHotlist();
- } else {
- BssidInfo[] bssidInfos = new BssidInfo[num_hotlist_ap];
- int apLostThreshold = Integer.MAX_VALUE;
- int index = 0;
- for (WifiScanner.HotlistSettings s : settings) {
- for (int i = 0; i < s.bssidInfos.length; i++, index++) {
- bssidInfos[index] = s.bssidInfos[i];
- }
- if (s.apLostThreshold < apLostThreshold) {
- apLostThreshold = s.apLostThreshold;
- }
- }
-
- WifiScanner.HotlistSettings mergedSettings = new WifiScanner.HotlistSettings();
- mergedSettings.bssidInfos = bssidInfos;
- mergedSettings.apLostThreshold = apLostThreshold;
- mScannerImpl.setHotlist(mergedSettings, this);
- }
- }
-
- private void reportHotlistResults(int what, ScanResult[] results) {
- if (DBG) localLog("reportHotlistResults " + what + " results " + results.length);
- for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) {
- ClientInfo ci = entry.clientInfo;
- int handler = entry.handlerId;
- WifiScanner.HotlistSettings settings = entry.settings;
- int num_results = 0;
- for (ScanResult result : results) {
- for (BssidInfo BssidInfo : settings.bssidInfos) {
- if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
- num_results++;
- break;
- }
- }
- }
- if (num_results == 0) {
- // nothing to report
- return;
- }
- ScanResult[] results2 = new ScanResult[num_results];
- int index = 0;
- for (ScanResult result : results) {
- for (BssidInfo BssidInfo : settings.bssidInfos) {
- if (result.BSSID.equalsIgnoreCase(BssidInfo.bssid)) {
- results2[index] = result;
- index++;
- }
- }
- }
- WifiScanner.ParcelableScanResults parcelableScanResults =
- new WifiScanner.ParcelableScanResults(results2);
-
- ci.reportEvent(what, 0, handler, parcelableScanResults);
- }
- }
-
- private void sendHotlistFailedToAllAndClear(int reason, String description) {
- for (RequestInfo<WifiScanner.HotlistSettings> entry : mActiveHotlistSettings) {
- ClientInfo ci = entry.clientInfo;
- int handler = entry.handlerId;
- ci.reportEvent(WifiScanner.CMD_OP_FAILED, 0, handler,
- new WifiScanner.OperationResult(reason, description));
- }
- mActiveHotlistSettings.clear();
- }
}
/**
@@ -1409,14 +1348,14 @@
* 2. Move to |Started State| when we get the |WIFI_SCAN_AVAILABLE| broadcast from WifiManager.
* 3. When a new PNO scan request comes in:
* a.1. Switch to |Hw Pno Scan state| when the device supports HW PNO
- * (This could either be HAL based ePNO or supplicant based PNO).
+ * (This could either be HAL based ePNO or wificond based PNO).
* a.2. In |Hw Pno Scan state| when PNO scan results are received, check if the result
* contains IE (information elements). If yes, send the results to the client, else
* switch to |Single Scan state| and send the result to the client when the scan result
* is obtained.
* b.1. Switch to |Sw Pno Scan state| when the device does not supports HW PNO
* (This is for older devices which do not support HW PNO and for connected PNO on
- * devices which support supplicant based PNO)
+ * devices which support wificond based PNO)
* b.2. In |Sw Pno Scan state| send the result to the client when the background scan result
* is obtained
*
@@ -1439,7 +1378,7 @@
WifiPnoScanStateMachine(Looper looper) {
super("WifiPnoScanStateMachine", looper);
- setLogRecSize(512);
+ setLogRecSize(256);
setLogOnlyTransitions(false);
// CHECKSTYLE:OFF IndentationCheck
@@ -1705,8 +1644,10 @@
}
}
- private WifiNative.PnoSettings convertPnoSettingsToNative(PnoSettings pnoSettings) {
+ private WifiNative.PnoSettings convertSettingsToPnoNative(ScanSettings scanSettings,
+ PnoSettings pnoSettings) {
WifiNative.PnoSettings nativePnoSetting = new WifiNative.PnoSettings();
+ nativePnoSetting.periodInMs = scanSettings.periodInMs;
nativePnoSetting.min5GHzRssi = pnoSettings.min5GHzRssi;
nativePnoSetting.min24GHzRssi = pnoSettings.min24GHzRssi;
nativePnoSetting.initialScoreMax = pnoSettings.initialScoreMax;
@@ -1720,8 +1661,6 @@
for (int i = 0; i < pnoSettings.networkList.length; i++) {
nativePnoSetting.networkList[i] = new WifiNative.PnoNetwork();
nativePnoSetting.networkList[i].ssid = pnoSettings.networkList[i].ssid;
- nativePnoSetting.networkList[i].networkId = pnoSettings.networkList[i].networkId;
- nativePnoSetting.networkList[i].priority = pnoSettings.networkList[i].priority;
nativePnoSetting.networkList[i].flags = pnoSettings.networkList[i].flags;
nativePnoSetting.networkList[i].auth_bit_field =
pnoSettings.networkList[i].authBitField;
@@ -1778,7 +1717,8 @@
loge("Failing scan request because there is already an active scan");
return false;
}
- WifiNative.PnoSettings nativePnoSettings = convertPnoSettingsToNative(pnoSettings);
+ WifiNative.PnoSettings nativePnoSettings =
+ convertSettingsToPnoNative(scanSettings, pnoSettings);
if (!mScannerImpl.setHwPnoList(nativePnoSettings, mPnoScanStateMachine)) {
return false;
}
@@ -1912,7 +1852,6 @@
mSingleScanListeners.removeAllForClient(this);
mSingleScanStateMachine.removeSingleScanRequests(this);
mBackgroundScanStateMachine.removeBackgroundScanSettings(this);
- mBackgroundScanStateMachine.removeHotlistSettings(this);
unregister();
localLog("Successfully stopped all requests for client " + this);
}
@@ -2012,10 +1951,6 @@
@Override
public void cleanup() {
mDisconnected = true;
- // Internal clients should not have any wifi change requests. So, keeping this cleanup
- // only for external client because this will otherwise cause an infinite recursion
- // when the internal client in WifiChangeStateMachine is cleaned up.
- mWifiChangeStateMachine.removeWifiChangeHandler(this);
mPnoScanStateMachine.removePnoSettings(this);
super.cleanup();
}
@@ -2090,8 +2025,12 @@
Message reply = Message.obtain();
reply.what = WifiScanner.CMD_OP_SUCCEEDED;
reply.arg2 = msg.arg2;
+ if (msg.obj != null) {
+ reply.obj = msg.obj;
+ }
try {
msg.replyTo.send(reply);
+ mLog.trace("replySucceeded recvdMessage=%").c(msg.what).flush();
} catch (RemoteException e) {
// There's not much we can do if reply can't be sent!
}
@@ -2108,6 +2047,10 @@
reply.obj = new WifiScanner.OperationResult(reason, description);
try {
msg.replyTo.send(reply);
+ mLog.trace("replyFailed recvdMessage=% reason=%")
+ .c(msg.what)
+ .c(reason)
+ .flush();
} catch (RemoteException e) {
// There's not much we can do if reply can't be sent!
}
@@ -2116,451 +2059,6 @@
}
}
- /**
- * Wifi Change state machine is used to handle any wifi change tracking requests.
- * TODO: This state machine doesn't handle driver loading/unloading yet.
- */
- class WifiChangeStateMachine extends StateMachine
- implements WifiNative.SignificantWifiChangeEventHandler {
-
- private static final int MAX_APS_TO_TRACK = 3;
- private static final int MOVING_SCAN_PERIOD_MS = 10000;
- private static final int STATIONARY_SCAN_PERIOD_MS = 5000;
- private static final int MOVING_STATE_TIMEOUT_MS = 30000;
-
- State mDefaultState = new DefaultState();
- State mStationaryState = new StationaryState();
- State mMovingState = new MovingState();
-
- private static final String ACTION_TIMEOUT =
- "com.android.server.WifiScanningServiceImpl.action.TIMEOUT";
- private PendingIntent mTimeoutIntent;
- private ScanResult[] mCurrentBssids;
- private InternalClientInfo mInternalClientInfo;
-
- private final Set<Pair<ClientInfo, Integer>> mActiveWifiChangeHandlers = new HashSet<>();
-
- WifiChangeStateMachine(Looper looper) {
- super("SignificantChangeStateMachine", looper);
-
- // CHECKSTYLE:OFF IndentationCheck
- addState(mDefaultState);
- addState(mStationaryState, mDefaultState);
- addState(mMovingState, mDefaultState);
- // CHECKSTYLE:ON IndentationCheck
-
- setInitialState(mDefaultState);
- }
-
- public void removeWifiChangeHandler(ClientInfo ci) {
- Iterator<Pair<ClientInfo, Integer>> iter = mActiveWifiChangeHandlers.iterator();
- while (iter.hasNext()) {
- Pair<ClientInfo, Integer> entry = iter.next();
- if (entry.first == ci) {
- iter.remove();
- }
- }
- untrackSignificantWifiChangeOnEmpty();
- }
-
- class DefaultState extends State {
- @Override
- public void enter() {
- if (DBG) localLog("Entering IdleState");
- }
-
- @Override
- public boolean processMessage(Message msg) {
- if (DBG) localLog("DefaultState state got " + msg);
- ClientInfo ci = mClients.get(msg.replyTo);
- switch (msg.what) {
- case WifiScanner.CMD_START_TRACKING_CHANGE:
- if (addWifiChangeHandler(ci, msg.arg2)) {
- replySucceeded(msg);
- transitionTo(mMovingState);
- } else {
- replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
- }
- break;
- case WifiScanner.CMD_STOP_TRACKING_CHANGE:
- // nothing to do
- break;
- case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
- /* save configuration till we transition to moving state */
- deferMessage(msg);
- break;
- case WifiScanner.CMD_SCAN_RESULT:
- // nothing to do
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class StationaryState extends State {
- @Override
- public void enter() {
- if (DBG) localLog("Entering StationaryState");
- reportWifiStabilized(mCurrentBssids);
- }
-
- @Override
- public boolean processMessage(Message msg) {
- if (DBG) localLog("Stationary state got " + msg);
- ClientInfo ci = mClients.get(msg.replyTo);
- switch (msg.what) {
- case WifiScanner.CMD_START_TRACKING_CHANGE:
- if (addWifiChangeHandler(ci, msg.arg2)) {
- replySucceeded(msg);
- } else {
- replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
- }
- break;
- case WifiScanner.CMD_STOP_TRACKING_CHANGE:
- removeWifiChangeHandler(ci, msg.arg2);
- break;
- case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
- /* save configuration till we transition to moving state */
- deferMessage(msg);
- break;
- case CMD_WIFI_CHANGE_DETECTED:
- if (DBG) localLog("Got wifi change detected");
- reportWifiChanged((ScanResult[]) msg.obj);
- transitionTo(mMovingState);
- break;
- case WifiScanner.CMD_SCAN_RESULT:
- // nothing to do
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- class MovingState extends State {
- boolean mWifiChangeDetected = false;
- boolean mScanResultsPending = false;
-
- @Override
- public void enter() {
- if (DBG) localLog("Entering MovingState");
- if (mTimeoutIntent == null) {
- Intent intent = new Intent(ACTION_TIMEOUT, null);
- mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
-
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- sendMessage(CMD_WIFI_CHANGE_TIMEOUT);
- }
- }, new IntentFilter(ACTION_TIMEOUT));
- }
- issueFullScan();
- }
-
- @Override
- public boolean processMessage(Message msg) {
- if (DBG) localLog("MovingState state got " + msg);
- ClientInfo ci = mClients.get(msg.replyTo);
- switch (msg.what) {
- case WifiScanner.CMD_START_TRACKING_CHANGE:
- if (addWifiChangeHandler(ci, msg.arg2)) {
- replySucceeded(msg);
- } else {
- replyFailed(msg, WifiScanner.REASON_INVALID_REQUEST, "bad request");
- }
- break;
- case WifiScanner.CMD_STOP_TRACKING_CHANGE:
- removeWifiChangeHandler(ci, msg.arg2);
- break;
- case WifiScanner.CMD_CONFIGURE_WIFI_CHANGE:
- if (DBG) localLog("Got configuration from app");
- WifiScanner.WifiChangeSettings settings =
- (WifiScanner.WifiChangeSettings) msg.obj;
- reconfigureScan(settings);
- mWifiChangeDetected = false;
- long unchangedDelay = settings.unchangedSampleSize * settings.periodInMs;
- mAlarmManager.cancel(mTimeoutIntent);
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + unchangedDelay,
- mTimeoutIntent);
- break;
- case WifiScanner.CMD_SCAN_RESULT:
- if (DBG) localLog("Got scan results");
- if (mScanResultsPending) {
- if (DBG) localLog("reconfiguring scan");
- reconfigureScan((ScanData[])msg.obj,
- STATIONARY_SCAN_PERIOD_MS);
- mWifiChangeDetected = false;
- mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mClock.elapsedRealtime() + MOVING_STATE_TIMEOUT_MS,
- mTimeoutIntent);
- mScanResultsPending = false;
- }
- break;
- case CMD_WIFI_CHANGE_DETECTED:
- if (DBG) localLog("Change detected");
- mAlarmManager.cancel(mTimeoutIntent);
- reportWifiChanged((ScanResult[])msg.obj);
- mWifiChangeDetected = true;
- issueFullScan();
- break;
- case CMD_WIFI_CHANGE_TIMEOUT:
- if (DBG) localLog("Got timeout event");
- if (mWifiChangeDetected == false) {
- transitionTo(mStationaryState);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
-
- @Override
- public void exit() {
- mAlarmManager.cancel(mTimeoutIntent);
- }
-
- void issueFullScan() {
- if (DBG) localLog("Issuing full scan");
- ScanSettings settings = new ScanSettings();
- settings.band = WifiScanner.WIFI_BAND_BOTH;
- settings.periodInMs = MOVING_SCAN_PERIOD_MS;
- settings.reportEvents = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
- addScanRequest(settings);
- mScanResultsPending = true;
- }
-
- }
-
- private void reconfigureScan(ScanData[] results, int period) {
- // find brightest APs and set them as sentinels
- if (results.length < MAX_APS_TO_TRACK) {
- localLog("too few APs (" + results.length + ") available to track wifi change");
- return;
- }
-
- removeScanRequest();
-
- // remove duplicate BSSIDs
- HashMap<String, ScanResult> bssidToScanResult = new HashMap<String, ScanResult>();
- for (ScanResult result : results[0].getResults()) {
- ScanResult saved = bssidToScanResult.get(result.BSSID);
- if (saved == null) {
- bssidToScanResult.put(result.BSSID, result);
- } else if (saved.level > result.level) {
- bssidToScanResult.put(result.BSSID, result);
- }
- }
-
- // find brightest BSSIDs
- ScanResult brightest[] = new ScanResult[MAX_APS_TO_TRACK];
- Collection<ScanResult> results2 = bssidToScanResult.values();
- for (ScanResult result : results2) {
- for (int j = 0; j < brightest.length; j++) {
- if (brightest[j] == null
- || (brightest[j].level < result.level)) {
- for (int k = brightest.length; k > (j + 1); k--) {
- brightest[k - 1] = brightest[k - 2];
- }
- brightest[j] = result;
- break;
- }
- }
- }
-
- // Get channels to scan for
- ArrayList<Integer> channels = new ArrayList<Integer>();
- for (int i = 0; i < brightest.length; i++) {
- boolean found = false;
- for (int j = i + 1; j < brightest.length; j++) {
- if (brightest[j].frequency == brightest[i].frequency) {
- found = true;
- }
- }
- if (!found) {
- channels.add(brightest[i].frequency);
- }
- }
-
- if (DBG) localLog("Found " + channels.size() + " channels");
-
- // set scanning schedule
- ScanSettings settings = new ScanSettings();
- settings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
- settings.channels = new ChannelSpec[channels.size()];
- for (int i = 0; i < channels.size(); i++) {
- settings.channels[i] = new ChannelSpec(channels.get(i));
- }
-
- settings.periodInMs = period;
- addScanRequest(settings);
-
- WifiScanner.WifiChangeSettings settings2 = new WifiScanner.WifiChangeSettings();
- settings2.rssiSampleSize = 3;
- settings2.lostApSampleSize = 3;
- settings2.unchangedSampleSize = 3;
- settings2.minApsBreachingThreshold = 2;
- settings2.bssidInfos = new BssidInfo[brightest.length];
-
- for (int i = 0; i < brightest.length; i++) {
- BssidInfo BssidInfo = new BssidInfo();
- BssidInfo.bssid = brightest[i].BSSID;
- int threshold = (100 + brightest[i].level) / 32 + 2;
- BssidInfo.low = brightest[i].level - threshold;
- BssidInfo.high = brightest[i].level + threshold;
- settings2.bssidInfos[i] = BssidInfo;
-
- if (DBG) localLog("Setting bssid=" + BssidInfo.bssid + ", " +
- "low=" + BssidInfo.low + ", high=" + BssidInfo.high);
- }
-
- trackSignificantWifiChange(settings2);
- mCurrentBssids = brightest;
- }
-
- private void reconfigureScan(WifiScanner.WifiChangeSettings settings) {
-
- if (settings.bssidInfos.length < MAX_APS_TO_TRACK) {
- localLog("too few APs (" + settings.bssidInfos.length
- + ") available to track wifi change");
- return;
- }
-
- if (DBG) localLog("Setting configuration specified by app");
-
- mCurrentBssids = new ScanResult[settings.bssidInfos.length];
- HashSet<Integer> channels = new HashSet<Integer>();
-
- for (int i = 0; i < settings.bssidInfos.length; i++) {
- ScanResult result = new ScanResult();
- result.BSSID = settings.bssidInfos[i].bssid;
- mCurrentBssids[i] = result;
- channels.add(settings.bssidInfos[i].frequencyHint);
- }
-
- // cancel previous scan
- removeScanRequest();
-
- // set new scanning schedule
- ScanSettings settings2 = new ScanSettings();
- settings2.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
- settings2.channels = new ChannelSpec[channels.size()];
- int i = 0;
- for (Integer channel : channels) {
- settings2.channels[i++] = new ChannelSpec(channel);
- }
-
- settings2.periodInMs = settings.periodInMs;
- addScanRequest(settings2);
-
- // start tracking new APs
- trackSignificantWifiChange(settings);
- }
-
-
- @Override
- public void onChangesFound(ScanResult results[]) {
- sendMessage(CMD_WIFI_CHANGE_DETECTED, 0, 0, results);
- }
-
- private void addScanRequest(ScanSettings settings) {
- if (DBG) localLog("Starting scans");
- if (mInternalClientInfo != null) {
- mInternalClientInfo.sendRequestToClientHandler(
- WifiScanner.CMD_START_BACKGROUND_SCAN, settings, null);
- }
- }
-
- private void removeScanRequest() {
- if (DBG) localLog("Stopping scans");
- if (mInternalClientInfo != null) {
- mInternalClientInfo.sendRequestToClientHandler(
- WifiScanner.CMD_STOP_BACKGROUND_SCAN);
- }
- }
-
- private void trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings) {
- if (mScannerImpl != null) {
- mScannerImpl.untrackSignificantWifiChange();
- mScannerImpl.trackSignificantWifiChange(settings, this);
- }
- }
-
- private void untrackSignificantWifiChange() {
- if (mScannerImpl != null) {
- mScannerImpl.untrackSignificantWifiChange();
- }
- }
-
- private boolean addWifiChangeHandler(ClientInfo ci, int handler) {
- if (ci == null) {
- Log.d(TAG, "Failing wifi change request ClientInfo not found " + handler);
- return false;
- }
- mActiveWifiChangeHandlers.add(Pair.create(ci, handler));
- // Add an internal client to make background scan requests.
- if (mInternalClientInfo == null) {
- mInternalClientInfo =
- new InternalClientInfo(ci.getUid(), new Messenger(this.getHandler()));
- mInternalClientInfo.register();
- }
- return true;
- }
-
- private void removeWifiChangeHandler(ClientInfo ci, int handler) {
- if (ci != null) {
- mActiveWifiChangeHandlers.remove(Pair.create(ci, handler));
- untrackSignificantWifiChangeOnEmpty();
- }
- }
-
- private void untrackSignificantWifiChangeOnEmpty() {
- if (mActiveWifiChangeHandlers.isEmpty()) {
- if (DBG) localLog("Got Disable Wifi Change");
- mCurrentBssids = null;
- untrackSignificantWifiChange();
- // Remove the internal client when there are no more external clients.
- if (mInternalClientInfo != null) {
- mInternalClientInfo.cleanup();
- mInternalClientInfo = null;
- }
- transitionTo(mDefaultState);
- }
- }
-
- private void reportWifiChanged(ScanResult[] results) {
- WifiScanner.ParcelableScanResults parcelableScanResults =
- new WifiScanner.ParcelableScanResults(results);
- Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator();
- while (it.hasNext()) {
- Pair<ClientInfo, Integer> entry = it.next();
- ClientInfo ci = entry.first;
- int handler = entry.second;
- ci.reportEvent(WifiScanner.CMD_WIFI_CHANGE_DETECTED, 0, handler,
- parcelableScanResults);
- }
- }
-
- private void reportWifiStabilized(ScanResult[] results) {
- WifiScanner.ParcelableScanResults parcelableScanResults =
- new WifiScanner.ParcelableScanResults(results);
- Iterator<Pair<ClientInfo, Integer>> it = mActiveWifiChangeHandlers.iterator();
- while (it.hasNext()) {
- Pair<ClientInfo, Integer> entry = it.next();
- ClientInfo ci = entry.first;
- int handler = entry.second;
- ci.reportEvent(WifiScanner.CMD_WIFI_CHANGES_STABILIZED, 0, handler,
- parcelableScanResults);
- }
- }
- }
-
private static String toString(int uid, ScanSettings settings) {
StringBuilder sb = new StringBuilder();
sb.append("ScanSettings[uid=").append(uid);
@@ -2624,6 +2122,40 @@
if (mPnoScanStateMachine != null) {
mPnoScanStateMachine.dump(fd, pw, args);
}
+ pw.println();
+
+ if (mSingleScanStateMachine != null) {
+ mSingleScanStateMachine.dump(fd, pw, args);
+ pw.println();
+ pw.println("Latest scan results:");
+ List<ScanResult> scanResults = mSingleScanStateMachine.getCachedScanResultsAsList();
+ long nowMs = System.currentTimeMillis();
+ if (scanResults != null && scanResults.size() != 0) {
+ pw.println(" BSSID Frequency RSSI Age(sec) SSID "
+ + " Flags");
+ for (ScanResult r : scanResults) {
+ String age;
+ if (r.seen <= 0) {
+ age = "___?___";
+ } else if (nowMs < r.seen) {
+ age = " 0.000";
+ } else if (r.seen < nowMs - 1000000) {
+ age = ">1000.0";
+ } else {
+ age = String.format("%3.3f", (nowMs - r.seen) / 1000.0);
+ }
+ String ssid = r.SSID == null ? "" : r.SSID;
+ pw.printf(" %17s %9d %5d %7s %-32s %s\n",
+ r.BSSID,
+ r.frequency,
+ r.level,
+ age,
+ String.format("%1.32s", ssid),
+ r.capabilities);
+ }
+ }
+ pw.println();
+ }
}
void logScanRequest(String request, ClientInfo ci, int id, WorkSource workSource,
@@ -2707,10 +2239,7 @@
.append(" networks:[ ");
if (pnoSettings.networkList != null) {
for (int i = 0; i < pnoSettings.networkList.length; i++) {
- sb.append(pnoSettings.networkList[i].ssid)
- .append(",")
- .append(pnoSettings.networkList[i].networkId)
- .append(" ");
+ sb.append(pnoSettings.networkList[i].ssid).append(",");
}
}
sb.append(" ] ")
diff --git a/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
similarity index 74%
rename from service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java
rename to service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
index f0cac0f..fd7fddb 100644
--- a/service/java/com/android/server/wifi/scanner/SupplicantWifiScannerImpl.java
+++ b/service/java/com/android/server/wifi/scanner/WificondScannerImpl.java
@@ -19,7 +19,6 @@
import android.app.AlarmManager;
import android.content.Context;
import android.net.wifi.ScanResult;
-import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiScanner;
import android.os.Handler;
import android.os.Looper;
@@ -42,25 +41,22 @@
import java.util.Set;
/**
- * Implementation of the WifiScanner HAL API that uses wpa_supplicant to perform all scans
+ * Implementation of the WifiScanner HAL API that uses wificond to perform all scans
* @see com.android.server.wifi.scanner.WifiScannerImpl for more details on each method.
*/
-public class SupplicantWifiScannerImpl extends WifiScannerImpl implements Handler.Callback {
- private static final String TAG = "SupplicantWifiScannerImpl";
+public class WificondScannerImpl extends WifiScannerImpl implements Handler.Callback {
+ private static final String TAG = "WificondScannerImpl";
private static final boolean DBG = false;
public static final String BACKGROUND_PERIOD_ALARM_TAG = TAG + " Background Scan Period";
public static final String TIMEOUT_ALARM_TAG = TAG + " Scan Timeout";
- // Max number of networks that can be specified to wpa_supplicant per scan request
+ // Max number of networks that can be specified to wificond per scan request
public static final int MAX_HIDDEN_NETWORK_IDS_PER_SCAN = 16;
private static final int SCAN_BUFFER_CAPACITY = 10;
private static final int MAX_APS_PER_SCAN = 32;
private static final int MAX_SCAN_BUCKETS = 16;
- private static final String ACTION_SCAN_PERIOD =
- "com.android.server.util.SupplicantWifiScannerImpl.action.SCAN_PERIOD";
-
private final Context mContext;
private final WifiNative mWifiNative;
private final AlarmManager mAlarmManager;
@@ -68,7 +64,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;
@@ -91,10 +87,6 @@
// Settings for the currently running scan, null if no scan active
private LastScanSettings mLastScanSettings = null;
- // Active hotlist settings
- private WifiNative.HotlistEventHandler mHotlistHandler = null;
- private ChangeBuffer mHotlistChangeBuffer = new ChangeBuffer();
-
// Pno related info.
private WifiNative.PnoSettings mPnoSettings = null;
private WifiNative.PnoEventHandler mPnoEventHandler;
@@ -131,8 +123,9 @@
}
};
- public SupplicantWifiScannerImpl(Context context, WifiNative wifiNative,
- ChannelHelper channelHelper, Looper looper, Clock clock) {
+ public WificondScannerImpl(Context context, WifiNative wifiNative,
+ WifiMonitor wifiMonitor, ChannelHelper channelHelper,
+ Looper looper, Clock clock) {
mContext = context;
mWifiNative = wifiNative;
mChannelHelper = channelHelper;
@@ -145,16 +138,18 @@
mHwPnoScanSupported = mContext.getResources().getBoolean(
R.bool.config_wifi_background_scan_support);
- WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(),
+ wifiMonitor.registerHandler(mWifiNative.getInterfaceName(),
WifiMonitor.SCAN_FAILED_EVENT, mEventHandler);
- WifiMonitor.getInstance().registerHandler(mWifiNative.getInterfaceName(),
+ wifiMonitor.registerHandler(mWifiNative.getInterfaceName(),
+ WifiMonitor.PNO_SCAN_RESULTS_EVENT, mEventHandler);
+ wifiMonitor.registerHandler(mWifiNative.getInterfaceName(),
WifiMonitor.SCAN_RESULTS_EVENT, mEventHandler);
}
- public SupplicantWifiScannerImpl(Context context, WifiNative wifiNative, Looper looper,
- Clock clock) {
- // TODO figure out how to get channel information from supplicant
- this(context, wifiNative, new NoBandChannelHelper(), looper, clock);
+ public WificondScannerImpl(Context context, WifiNative wifiNative,
+ WifiMonitor wifiMonitor, Looper looper, Clock clock) {
+ // TODO get channel information from wificond.
+ this(context, wifiNative, wifiMonitor, new NoBandChannelHelper(), looper, clock);
}
@Override
@@ -164,8 +159,6 @@
mPendingSingleScanEventHandler = null;
stopHwPnoScan();
stopBatchedScan();
- resetHotlist();
- untrackSignificantWifiChange();
mLastScanSettings = null; // finally clear any active scan
}
}
@@ -177,8 +170,6 @@
capabilities.max_ap_cache_per_scan = MAX_APS_PER_SCAN;
capabilities.max_rssi_sample_size = 8;
capabilities.max_scan_reporting_threshold = SCAN_BUFFER_CAPACITY;
- capabilities.max_hotlist_bssids = 0;
- capabilities.max_significant_wifi_change_aps = 0;
return true;
}
@@ -249,7 +240,7 @@
stopBatchedScan();
if (DBG) {
Log.d(TAG, "Starting scan num_buckets=" + settings.num_buckets + ", base_period="
- + settings.base_period_ms + " ms");
+ + settings.base_period_ms + " ms");
}
mPendingBackgroundScanSettings = settings;
mPendingBackgroundScanEventHandler = eventHandler;
@@ -324,7 +315,7 @@
}
private void handleScanTimeout() {
- Log.e(TAG, "Timed out waiting for scan result from supplicant");
+ Log.e(TAG, "Timed out waiting for scan result from wificond");
reportScanFailure();
processPendingScans();
}
@@ -344,9 +335,9 @@
}
ChannelCollection allFreqs = mChannelHelper.createChannelCollection();
- Set<Integer> hiddenNetworkIdSet = new HashSet<Integer>();
+ Set<String> hiddenNetworkSSIDSet = new HashSet<>();
final LastScanSettings newScanSettings =
- new LastScanSettings(mClock.elapsedRealtime());
+ new LastScanSettings(mClock.getElapsedSinceBootMillis());
// Update scan settings if there is a pending scan
if (!mBackgroundScanPaused) {
@@ -389,20 +380,11 @@
mBackgroundScanSettings.report_threshold_num_scans,
mBackgroundScanSettings.report_threshold_percent);
}
-
- int[] hiddenNetworkIds = mBackgroundScanSettings.hiddenNetworkIds;
- if (hiddenNetworkIds != null) {
- int numHiddenNetworkIds = Math.min(hiddenNetworkIds.length,
- MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
- for (int i = 0; i < numHiddenNetworkIds; i++) {
- hiddenNetworkIdSet.add(hiddenNetworkIds[i]);
- }
- }
-
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);
}
}
@@ -422,14 +404,17 @@
}
newScanSettings.setSingleScan(reportFullResults, singleScanFreqs,
mPendingSingleScanEventHandler);
- int[] hiddenNetworkIds = mPendingSingleScanSettings.hiddenNetworkIds;
- if (hiddenNetworkIds != null) {
- int numHiddenNetworkIds = Math.min(hiddenNetworkIds.length,
- MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
- for (int i = 0; i < numHiddenNetworkIds; i++) {
- hiddenNetworkIdSet.add(hiddenNetworkIds[i]);
+
+ WifiNative.HiddenNetwork[] hiddenNetworks =
+ mPendingSingleScanSettings.hiddenNetworks;
+ if (hiddenNetworks != null) {
+ int numHiddenNetworks =
+ Math.min(hiddenNetworks.length, MAX_HIDDEN_NETWORK_IDS_PER_SCAN);
+ for (int i = 0; i < numHiddenNetworks; i++) {
+ hiddenNetworkSSIDSet.add(hiddenNetworks[i].ssid);
}
}
+
mPendingSingleScanSettings = null;
mPendingSingleScanEventHandler = null;
}
@@ -437,8 +422,8 @@
if ((newScanSettings.backgroundScanActive || newScanSettings.singleScanActive)
&& !allFreqs.isEmpty()) {
pauseHwPnoScan();
- Set<Integer> freqs = allFreqs.getSupplicantScanFreqs();
- boolean success = mWifiNative.scan(freqs, hiddenNetworkIdSet);
+ Set<Integer> freqs = allFreqs.getScanFreqs();
+ boolean success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet);
if (success) {
// TODO handle scan timeout
if (DBG) {
@@ -448,7 +433,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);
@@ -469,9 +454,9 @@
// If the PNO network list has changed from the previous request, ensure that
// we bypass the debounce logic and restart PNO scan.
if (isDifferentPnoScanSettings(newScanSettings)) {
- status = restartHwPnoScan();
+ status = restartHwPnoScan(mPnoSettings);
} else {
- status = startHwPnoScan();
+ status = startHwPnoScan(mPnoSettings);
}
if (status) {
mLastScanSettings = newScanSettings;
@@ -502,6 +487,10 @@
reportScanFailure();
processPendingScans();
break;
+ case WifiMonitor.PNO_SCAN_RESULTS_EVENT:
+ pollLatestScanDataForPno();
+ processPendingScans();
+ break;
case WifiMonitor.SCAN_RESULTS_EVENT:
mAlarmManager.cancel(mScanTimeoutListener);
pollLatestScanData();
@@ -540,6 +529,67 @@
}
}
+ private void pollLatestScanDataForPno() {
+ synchronized (mSettingsLock) {
+ if (mLastScanSettings == null) {
+ // got a scan before we started scanning or after scan was canceled
+ return;
+ }
+ ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults();
+ List<ScanResult> hwPnoScanResults = new ArrayList<>();
+ int numFilteredScanResults = 0;
+ for (int i = 0; i < nativeResults.size(); ++i) {
+ ScanResult result = nativeResults.get(i).getScanResult();
+ long timestamp_ms = result.timestamp / 1000; // convert us -> ms
+ if (timestamp_ms > mLastScanSettings.startTime) {
+ if (mLastScanSettings.hwPnoScanActive) {
+ hwPnoScanResults.add(result);
+ }
+ } else {
+ numFilteredScanResults++;
+ }
+ }
+
+ if (numFilteredScanResults != 0) {
+ Log.d(TAG, "Filtering out " + numFilteredScanResults + " pno scan results.");
+ }
+
+ if (mLastScanSettings.hwPnoScanActive
+ && mLastScanSettings.pnoScanEventHandler != null) {
+ ScanResult[] pnoScanResultsArray = new ScanResult[hwPnoScanResults.size()];
+ for (int i = 0; i < pnoScanResultsArray.length; ++i) {
+ ScanResult result = nativeResults.get(i).getScanResult();
+ pnoScanResultsArray[i] = hwPnoScanResults.get(i);
+ }
+ mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
+ }
+ // On pno scan result event, we are expecting a mLastScanSettings for pno scan.
+ // However, if unlikey mLastScanSettings is for single scan, we need this part
+ // to protect from leaving WifiSingleScanStateMachine in a forever wait state.
+ if (mLastScanSettings.singleScanActive
+ && mLastScanSettings.singleScanEventHandler != null) {
+ Log.w(TAG, "Polling pno scan result when single scan is active, reporting"
+ + " single scan failure");
+ mLastScanSettings.singleScanEventHandler
+ .onScanStatus(WifiNative.WIFI_SCAN_FAILED);
+ }
+ // mLastScanSettings is for either single/batched scan or pno scan.
+ // We can safely set it to null when pno scan finishes.
+ mLastScanSettings = null;
+ }
+ }
+
+ /**
+ * Check if the provided channel collection contains all the channels.
+ */
+ private static boolean isAllChannelsScanned(ChannelCollection channelCollection) {
+ // TODO(b/62253332): Get rid of this hack.
+ // We're treating 2g + 5g and 2g + 5g + dfs as all channels scanned to work around
+ // the lack of a proper cache.
+ return (channelCollection.containsBand(WifiScanner.WIFI_BAND_24_GHZ)
+ && channelCollection.containsBand(WifiScanner.WIFI_BAND_5_GHZ));
+ }
+
private void pollLatestScanData() {
synchronized (mSettingsLock) {
if (mLastScanSettings == null) {
@@ -551,7 +601,7 @@
ArrayList<ScanDetail> nativeResults = mWifiNative.getScanResults();
List<ScanResult> singleScanResults = new ArrayList<>();
List<ScanResult> backgroundScanResults = new ArrayList<>();
- List<ScanResult> hwPnoScanResults = new ArrayList<>();
+ int numFilteredScanResults = 0;
for (int i = 0; i < nativeResults.size(); ++i) {
ScanResult result = nativeResults.get(i).getScanResult();
long timestamp_ms = result.timestamp / 1000; // convert us -> ms
@@ -564,13 +614,13 @@
result.frequency)) {
singleScanResults.add(result);
}
- if (mLastScanSettings.hwPnoScanActive) {
- hwPnoScanResults.add(result);
- }
} else {
- // was a cached result in wpa_supplicant
+ numFilteredScanResults++;
}
}
+ if (numFilteredScanResults != 0) {
+ Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results.");
+ }
if (mLastScanSettings.backgroundScanActive) {
if (mBackgroundScanEventHandler != null) {
@@ -613,18 +663,6 @@
.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
}
}
-
- if (mHotlistHandler != null) {
- int event = mHotlistChangeBuffer.processScan(backgroundScanResults);
- if ((event & ChangeBuffer.EVENT_FOUND) != 0) {
- mHotlistHandler.onHotlistApFound(
- mHotlistChangeBuffer.getLastResults(ChangeBuffer.EVENT_FOUND));
- }
- if ((event & ChangeBuffer.EVENT_LOST) != 0) {
- mHotlistHandler.onHotlistApLost(
- mHotlistChangeBuffer.getLastResults(ChangeBuffer.EVENT_LOST));
- }
- }
}
if (mLastScanSettings.singleScanActive
@@ -638,21 +676,12 @@
}
Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR);
mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 0,
- mLastScanSettings.singleScanFreqs.isAllChannels(),
+ isAllChannelsScanned(mLastScanSettings.singleScanFreqs),
singleScanResults.toArray(new ScanResult[singleScanResults.size()]));
mLastScanSettings.singleScanEventHandler
.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
}
- if (mLastScanSettings.hwPnoScanActive
- && mLastScanSettings.pnoScanEventHandler != null) {
- ScanResult[] pnoScanResultsArray = new ScanResult[hwPnoScanResults.size()];
- for (int i = 0; i < pnoScanResultsArray.length; ++i) {
- pnoScanResultsArray[i] = hwPnoScanResults.get(i);
- }
- mLastScanSettings.pnoScanEventHandler.onPnoNetworkFound(pnoScanResultsArray);
- }
-
mLastScanSettings = null;
}
}
@@ -669,27 +698,8 @@
}
}
- private boolean setNetworkPriorities(WifiNative.PnoNetwork[] networkList) {
- if (networkList != null) {
- if (DBG) Log.i(TAG, "Enable network and Set priorities for PNO.");
- for (WifiNative.PnoNetwork network : networkList) {
- if (!mWifiNative.setNetworkVariable(network.networkId,
- WifiConfiguration.priorityVarName,
- Integer.toString(network.priority))) {
- Log.e(TAG, "Set priority failed for: " + network.networkId);
- return false;
- }
- if (!mWifiNative.enableNetworkWithoutConnect(network.networkId)) {
- Log.e(TAG, "Enable network failed for: " + network.networkId);
- return false;
- }
- }
- }
- return true;
- }
-
- private boolean startHwPnoScan() {
- return mHwPnoDebouncer.startPnoScan(mHwPnoDebouncerListener);
+ private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) {
+ return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener);
}
private void stopHwPnoScan() {
@@ -700,9 +710,9 @@
mHwPnoDebouncer.forceStopPnoScan();
}
- private boolean restartHwPnoScan() {
+ private boolean restartHwPnoScan(WifiNative.PnoSettings pnoSettings) {
mHwPnoDebouncer.forceStopPnoScan();
- return mHwPnoDebouncer.startPnoScan(mHwPnoDebouncerListener);
+ return mHwPnoDebouncer.startPnoScan(pnoSettings, mHwPnoDebouncerListener);
}
/**
@@ -729,8 +739,8 @@
}
mPnoEventHandler = eventHandler;
mPnoSettings = settings;
- if (!setNetworkPriorities(settings.networkList)) return false;
- // For supplicant based PNO, we start the scan immediately when we set pno list.
+
+ // For wificond based PNO, we start the scan immediately when we set pno list.
processPendingScans();
return true;
}
@@ -745,7 +755,7 @@
}
mPnoEventHandler = null;
mPnoSettings = null;
- // For supplicant based PNO, we stop the scan immediately when we reset pno list.
+ // For wificond based PNO, we stop the scan immediately when we reset pno list.
stopHwPnoScan();
return true;
}
@@ -762,43 +772,10 @@
return false;
}
- @Override
- public boolean setHotlist(WifiScanner.HotlistSettings settings,
- WifiNative.HotlistEventHandler eventHandler) {
- if (settings == null || eventHandler == null) {
- return false;
- }
- synchronized (mSettingsLock) {
- mHotlistHandler = eventHandler;
- mHotlistChangeBuffer.setSettings(settings.bssidInfos, settings.apLostThreshold, 1);
- return true;
- }
- }
-
- @Override
- public void resetHotlist() {
- synchronized (mSettingsLock) {
- mHotlistChangeBuffer.clearSettings();
- mHotlistHandler = null;
- }
- }
-
- /*
- * Significant Wifi Change API is not implemented
- */
- @Override
- public boolean trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings,
- WifiNative.SignificantWifiChangeEventHandler handler) {
- return false;
- }
- @Override
- public void untrackSignificantWifiChange() {}
-
-
private static class LastScanSettings {
public long startTime;
- public LastScanSettings(long startTime) {
+ LastScanSettings(long startTime) {
this.startTime = startTime;
}
@@ -853,7 +830,7 @@
private final ArrayDeque<WifiScanner.ScanData> mBuffer;
private int mCapacity;
- public ScanBuffer(int capacity) {
+ ScanBuffer(int capacity) {
mCapacity = capacity;
mBuffer = new ArrayDeque<>(mCapacity);
}
@@ -886,138 +863,6 @@
}
}
- private static class ChangeBuffer {
- public static int EVENT_NONE = 0;
- public static int EVENT_LOST = 1;
- public static int EVENT_FOUND = 2;
-
- public static int STATE_FOUND = 0;
-
- private WifiScanner.BssidInfo[] mBssidInfos = null;
- private int mApLostThreshold;
- private int mMinEvents;
- private int[] mLostCount = null;
- private ScanResult[] mMostRecentResult = null;
- private int[] mPendingEvent = null;
- private boolean mFiredEvents = false;
-
- private static ScanResult findResult(List<ScanResult> results, String bssid) {
- for (int i = 0; i < results.size(); ++i) {
- if (bssid.equalsIgnoreCase(results.get(i).BSSID)) {
- return results.get(i);
- }
- }
- return null;
- }
-
- public void setSettings(WifiScanner.BssidInfo[] bssidInfos, int apLostThreshold,
- int minEvents) {
- mBssidInfos = bssidInfos;
- if (apLostThreshold <= 0) {
- mApLostThreshold = 1;
- } else {
- mApLostThreshold = apLostThreshold;
- }
- mMinEvents = minEvents;
- if (bssidInfos != null) {
- mLostCount = new int[bssidInfos.length];
- Arrays.fill(mLostCount, mApLostThreshold); // default to lost
- mMostRecentResult = new ScanResult[bssidInfos.length];
- mPendingEvent = new int[bssidInfos.length];
- mFiredEvents = false;
- } else {
- mLostCount = null;
- mMostRecentResult = null;
- mPendingEvent = null;
- }
- }
-
- public void clearSettings() {
- setSettings(null, 0, 0);
- }
-
- /**
- * Get the most recent scan results for APs that triggered the given event on the last call
- * to {@link #processScan}.
- */
- public ScanResult[] getLastResults(int event) {
- ArrayList<ScanResult> results = new ArrayList<>();
- for (int i = 0; i < mLostCount.length; ++i) {
- if (mPendingEvent[i] == event) {
- results.add(mMostRecentResult[i]);
- }
- }
- return results.toArray(new ScanResult[results.size()]);
- }
-
- /**
- * Process the supplied scan results and determine if any events should be generated based
- * on the configured settings
- * @return The events that occurred
- */
- public int processScan(List<ScanResult> scanResults) {
- if (mBssidInfos == null) {
- return EVENT_NONE;
- }
-
- // clear events from last time
- if (mFiredEvents) {
- mFiredEvents = false;
- for (int i = 0; i < mLostCount.length; ++i) {
- mPendingEvent[i] = EVENT_NONE;
- }
- }
-
- int eventCount = 0;
- int eventType = EVENT_NONE;
- for (int i = 0; i < mLostCount.length; ++i) {
- ScanResult result = findResult(scanResults, mBssidInfos[i].bssid);
- int rssi = Integer.MIN_VALUE;
- if (result != null) {
- mMostRecentResult[i] = result;
- rssi = result.level;
- }
-
- if (rssi < mBssidInfos[i].low) {
- if (mLostCount[i] < mApLostThreshold) {
- mLostCount[i]++;
-
- if (mLostCount[i] >= mApLostThreshold) {
- if (mPendingEvent[i] == EVENT_FOUND) {
- mPendingEvent[i] = EVENT_NONE;
- } else {
- mPendingEvent[i] = EVENT_LOST;
- }
- }
- }
- } else {
- if (mLostCount[i] >= mApLostThreshold) {
- if (mPendingEvent[i] == EVENT_LOST) {
- mPendingEvent[i] = EVENT_NONE;
- } else {
- mPendingEvent[i] = EVENT_FOUND;
- }
- }
- mLostCount[i] = STATE_FOUND;
- }
- if (DBG) {
- Log.d(TAG, "ChangeBuffer BSSID: " + mBssidInfos[i].bssid + "=" + mLostCount[i]
- + ", " + mPendingEvent[i] + ", rssi=" + rssi);
- }
- if (mPendingEvent[i] != EVENT_NONE) {
- ++eventCount;
- eventType |= mPendingEvent[i];
- }
- }
- if (DBG) Log.d(TAG, "ChangeBuffer events count=" + eventCount + ": " + eventType);
- if (eventCount >= mMinEvents) {
- mFiredEvents = true;
- return eventType;
- }
- return EVENT_NONE;
- }
- }
-
/**
* HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO
* state too often which is not handled very well by some drivers.
@@ -1036,6 +881,7 @@
private boolean mCurrentPnoState = false;;
private boolean mWaitForTimer = false;
private Listener mListener;
+ private WifiNative.PnoSettings mPnoSettings;
/**
* Interface used to indicate PNO scan notifications.
@@ -1056,34 +902,63 @@
}
/**
- * Enable/Disable PNO state in wpa_supplicant
- * @param enable boolean indicating whether PNO is being enabled or disabled.
+ * Enable PNO state in wificond
*/
- private boolean updatePnoState(boolean enable) {
- if (mCurrentPnoState == enable) {
- if (DBG) Log.d(TAG, "PNO state is already " + enable);
+ private boolean startPnoScanInternal() {
+ if (mCurrentPnoState) {
+ if (DBG) Log.d(TAG, "PNO state is already enable");
return true;
}
- mLastPnoChangeTimeStamp = mClock.elapsedRealtime();
- if (mWifiNative.setPnoScan(enable)) {
- Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to " + enable);
- mCurrentPnoState = enable;
- return true;
- } else {
- Log.e(TAG, "PNO state change to " + enable + " failed");
- mCurrentPnoState = false;
+ if (mPnoSettings == null) {
+ Log.e(TAG, "PNO state change to enable failed, no available Pno settings");
return false;
}
+ mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis();
+ Log.d(TAG, "Remove all networks from supplicant before starting PNO scan");
+ mWifiNative.removeAllNetworks();
+ if (mWifiNative.startPnoScan(mPnoSettings)) {
+ Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to enable");
+ mCurrentPnoState = true;
+ return true;
+ } else {
+ Log.e(TAG, "PNO state change to enable failed");
+ mCurrentPnoState = false;
+ }
+ return false;
+ }
+
+ /**
+ * Disable PNO state in wificond
+ */
+ private boolean stopPnoScanInternal() {
+ if (!mCurrentPnoState) {
+ if (DBG) Log.d(TAG, "PNO state is already disable");
+ return true;
+ }
+ mLastPnoChangeTimeStamp = mClock.getElapsedSinceBootMillis();
+ if (mWifiNative.stopPnoScan()) {
+ Log.d(TAG, "Changed PNO state from " + mCurrentPnoState + " to disable");
+ mCurrentPnoState = false;
+ return true;
+ } else {
+ Log.e(TAG, "PNO state change to disable failed");
+ mCurrentPnoState = false;
+ }
+ return false;
}
private final AlarmManager.OnAlarmListener mAlarmListener =
new AlarmManager.OnAlarmListener() {
public void onAlarm() {
if (DBG) Log.d(TAG, "PNO timer expired, expected state " + mExpectedPnoState);
- if (!updatePnoState(mExpectedPnoState)) {
- if (mListener != null) {
- mListener.onPnoScanFailed();
+ if (mExpectedPnoState) {
+ if (!startPnoScanInternal()) {
+ if (mListener != null) {
+ mListener.onPnoScanFailed();
+ }
}
+ } else {
+ stopPnoScanInternal();
}
mWaitForTimer = false;
}
@@ -1097,14 +972,19 @@
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);
+ if (enable) {
+ isSuccess = startPnoScanInternal();
+ } else {
+ isSuccess = stopPnoScanInternal();
+ }
} 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;
}
@@ -1115,9 +995,10 @@
/**
* Start PNO scan
*/
- public boolean startPnoScan(Listener listener) {
+ public boolean startPnoScan(WifiNative.PnoSettings pnoSettings, Listener listener) {
if (DBG) Log.d(TAG, "Starting PNO scan");
mListener = listener;
+ mPnoSettings = pnoSettings;
if (!setPnoState(true)) {
mListener = null;
return false;
@@ -1145,7 +1026,7 @@
mAlarmManager.cancel(mAlarmListener);
mWaitForTimer = false;
}
- updatePnoState(false);
+ stopPnoScanInternal();
}
}
}
diff --git a/service/java/com/android/server/wifi/util/BitMask.java b/service/java/com/android/server/wifi/util/BitMask.java
new file mode 100644
index 0000000..a6a82fd
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/BitMask.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.util;
+
+/**
+ * Helper for translating bit-flags packed into an int
+ */
+public class BitMask {
+ public int value;
+
+ public BitMask(int value) {
+ this.value = value;
+ }
+
+ /**
+ * Clears the specifed bit, returning true if it was set
+ *
+ * @param maskBit to test and clear
+ * @return true if and only if the mask was originally set in value
+ */
+ public boolean testAndClear(int maskBit) {
+ boolean ans = (value & maskBit) != 0;
+ value &= ~maskBit;
+ return ans;
+ }
+}
diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java
index d3e2fea..c8f9ca3 100644
--- a/service/java/com/android/server/wifi/util/InformationElementUtil.java
+++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java
@@ -15,16 +15,14 @@
*/
package com.android.server.wifi.util;
-import static com.android.server.wifi.anqp.Constants.getInteger;
-
+import android.net.wifi.ScanResult;
import android.net.wifi.ScanResult.InformationElement;
import android.util.Log;
-import com.android.server.wifi.anqp.Constants;
-import com.android.server.wifi.anqp.VenueNameElement;
+import com.android.server.wifi.ByteBufferReader;
import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.hotspot2.anqp.Constants;
-import java.net.ProtocolException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -32,6 +30,7 @@
import java.util.BitSet;
public class InformationElementUtil {
+ private static final String TAG = "InformationElementUtil";
public static InformationElement[] parseInformationElements(byte[] bytes) {
if (bytes == null) {
@@ -65,6 +64,71 @@
return infoElements.toArray(new InformationElement[infoElements.size()]);
}
+ /**
+ * Parse and retrieve the Roaming Consortium Information Element from the list of IEs.
+ *
+ * @param ies List of IEs to retrieve from
+ * @return {@link RoamingConsortium}
+ */
+ public static RoamingConsortium getRoamingConsortiumIE(InformationElement[] ies) {
+ RoamingConsortium roamingConsortium = new RoamingConsortium();
+ if (ies != null) {
+ for (InformationElement ie : ies) {
+ if (ie.id == InformationElement.EID_ROAMING_CONSORTIUM) {
+ try {
+ roamingConsortium.from(ie);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to parse Roaming Consortium IE: " + e.getMessage());
+ }
+ }
+ }
+ }
+ return roamingConsortium;
+ }
+
+ /**
+ * Parse and retrieve the Hotspot 2.0 Vendor Specific Information Element from the list of IEs.
+ *
+ * @param ies List of IEs to retrieve from
+ * @return {@link Vsa}
+ */
+ public static Vsa getHS2VendorSpecificIE(InformationElement[] ies) {
+ Vsa vsa = new Vsa();
+ if (ies != null) {
+ for (InformationElement ie : ies) {
+ if (ie.id == InformationElement.EID_VSA) {
+ try {
+ vsa.from(ie);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to parse Vendor Specific IE: " + e.getMessage());
+ }
+ }
+ }
+ }
+ return vsa;
+ }
+
+ /**
+ * Parse and retrieve the Interworking information element from the list of IEs.
+ *
+ * @param ies List of IEs to retrieve from
+ * @return {@link Interworking}
+ */
+ public static Interworking getInterworkingIE(InformationElement[] ies) {
+ Interworking interworking = new Interworking();
+ if (ies != null) {
+ for (InformationElement ie : ies) {
+ if (ie.id == InformationElement.EID_INTERWORKING) {
+ try {
+ interworking.from(ie);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to parse Interworking IE: " + e.getMessage());
+ }
+ }
+ }
+ }
+ return interworking;
+ }
public static class BssLoad {
public int stationCount = 0;
@@ -160,8 +224,6 @@
public static class Interworking {
public NetworkDetail.Ant ant = null;
public boolean internet = false;
- public VenueNameElement.VenueGroup venueGroup = null;
- public VenueNameElement.VenueType venueType = null;
public long hessid = 0L;
public void from(InformationElement ie) {
@@ -172,24 +234,21 @@
int anOptions = data.get() & Constants.BYTE_MASK;
ant = NetworkDetail.Ant.values()[anOptions & 0x0f];
internet = (anOptions & 0x10) != 0;
- // Len 1 none, 3 venue-info, 7 HESSID, 9 venue-info & HESSID
- if (ie.bytes.length == 3 || ie.bytes.length == 9) {
- try {
- ByteBuffer vinfo = data.duplicate();
- vinfo.limit(vinfo.position() + 2);
- VenueNameElement vne = new VenueNameElement(
- Constants.ANQPElementType.ANQPVenueName, vinfo);
- venueGroup = vne.getGroup();
- venueType = vne.getType();
- } catch (ProtocolException pe) {
- /*Cannot happen*/
- }
- } else if (ie.bytes.length != 1 && ie.bytes.length != 7) {
- throw new IllegalArgumentException("Bad Interworking element length: "
- + ie.bytes.length);
+ // There are only three possible lengths for the Interworking IE:
+ // Len 1: Access Network Options only
+ // Len 3: Access Network Options & Venue Info
+ // Len 7: Access Network Options & HESSID
+ // Len 9: Access Network Options, Venue Info, & HESSID
+ if (ie.bytes.length != 1
+ && ie.bytes.length != 3
+ && ie.bytes.length != 7
+ && ie.bytes.length != 9) {
+ throw new IllegalArgumentException(
+ "Bad Interworking element length: " + ie.bytes.length);
}
+
if (ie.bytes.length == 7 || ie.bytes.length == 9) {
- hessid = getInteger(data, ByteOrder.BIG_ENDIAN, 6);
+ hessid = ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, 6);
}
}
}
@@ -223,15 +282,15 @@
roamingConsortiums = new long[oiCount];
if (oi1Length > 0 && roamingConsortiums.length > 0) {
roamingConsortiums[0] =
- getInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
+ ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi1Length);
}
if (oi2Length > 0 && roamingConsortiums.length > 1) {
roamingConsortiums[1] =
- getInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
+ ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi2Length);
}
if (oi3Length > 0 && roamingConsortiums.length > 2) {
roamingConsortiums[2] =
- getInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
+ ByteBufferReader.readInteger(data, ByteOrder.BIG_ENDIAN, oi3Length);
}
}
}
@@ -268,37 +327,52 @@
}
}
+ /**
+ * This IE contained a bit field indicating the capabilities being advertised by the STA.
+ * The size of the bit field (number of bytes) is indicated by the |Length| field in the IE.
+ *
+ * Refer to Section 8.4.2.29 in IEEE 802.11-2012 Spec for capability associated with each
+ * bit.
+ *
+ * Here is the wire format of this IE:
+ * | Element ID | Length | Capabilities |
+ * 1 1 n
+ */
public static class ExtendedCapabilities {
private static final int RTT_RESP_ENABLE_BIT = 70;
- private static final long SSID_UTF8_BIT = 0x0001000000000000L;
+ private static final int SSID_UTF8_BIT = 48;
- public Long extendedCapabilities = null;
- public boolean is80211McRTTResponder = false;
+ public BitSet capabilitiesBitSet;
+
+ /**
+ * @return true if SSID should be interpreted using UTF-8 encoding
+ */
+ public boolean isStrictUtf8() {
+ return capabilitiesBitSet.get(SSID_UTF8_BIT);
+ }
+
+ /**
+ * @return true if 802.11 MC RTT Response is enabled
+ */
+ public boolean is80211McRTTResponder() {
+ return capabilitiesBitSet.get(RTT_RESP_ENABLE_BIT);
+ }
public ExtendedCapabilities() {
+ capabilitiesBitSet = new BitSet();
}
public ExtendedCapabilities(ExtendedCapabilities other) {
- extendedCapabilities = other.extendedCapabilities;
- is80211McRTTResponder = other.is80211McRTTResponder;
+ capabilitiesBitSet = other.capabilitiesBitSet;
}
- public boolean isStrictUtf8() {
- return extendedCapabilities != null && (extendedCapabilities & SSID_UTF8_BIT) != 0;
- }
-
+ /**
+ * Parse an ExtendedCapabilities from the IE containing raw bytes.
+ *
+ * @param ie The Information element data
+ */
public void from(InformationElement ie) {
- ByteBuffer data = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
- extendedCapabilities =
- Constants.getInteger(data, ByteOrder.LITTLE_ENDIAN, ie.bytes.length);
-
- int index = RTT_RESP_ENABLE_BIT / 8;
- byte offset = RTT_RESP_ENABLE_BIT % 8;
- if (ie.bytes.length < index + 1) {
- is80211McRTTResponder = false;
- } else {
- is80211McRTTResponder = (ie.bytes[index] & ((byte) 0x1 << offset)) != 0;
- }
+ capabilitiesBitSet = BitSet.valueOf(ie.bytes);
}
}
@@ -315,6 +389,7 @@
private static final int CAP_PRIVACY_BIT_OFFSET = 4;
private static final int WPA_VENDOR_OUI_TYPE_ONE = 0x01f25000;
+ private static final int WPS_VENDOR_OUI_TYPE = 0x04f25000;
private static final short WPA_VENDOR_OUI_VERSION = 0x0001;
private static final short RSNE_VERSION = 0x0001;
@@ -328,6 +403,23 @@
private static final int WPA2_AKM_EAP_SHA256 = 0x05ac0f00;
private static final int WPA2_AKM_PSK_SHA256 = 0x06ac0f00;
+ private static final int WPA_CIPHER_NONE = 0x00f25000;
+ private static final int WPA_CIPHER_TKIP = 0x02f25000;
+ private static final int WPA_CIPHER_CCMP = 0x04f25000;
+
+ private static final int RSN_CIPHER_NONE = 0x00ac0f00;
+ private static final int RSN_CIPHER_TKIP = 0x02ac0f00;
+ private static final int RSN_CIPHER_CCMP = 0x04ac0f00;
+ private static final int RSN_CIPHER_NO_GROUP_ADDRESSED = 0x07ac0f00;
+
+ public ArrayList<Integer> protocol;
+ public ArrayList<ArrayList<Integer>> keyManagement;
+ public ArrayList<ArrayList<Integer>> pairwiseCipher;
+ public ArrayList<Integer> groupCipher;
+ public boolean isESS;
+ public boolean isPrivacy;
+ public boolean isWPS;
+
public Capabilities() {
}
@@ -344,80 +436,112 @@
//
// Note: InformationElement.bytes has 'Element ID' and 'Length'
// stripped off already
- private static String parseRsnElement(InformationElement ie) {
+ private void parseRsnElement(InformationElement ie) {
ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
try {
// version
if (buf.getShort() != RSNE_VERSION) {
// incorrect version
- return null;
+ return;
}
- // group data cipher suite
- // here we simply advance the buffer position
- buf.getInt();
-
// found the RSNE IE, hence start building the capability string
- String security = "[WPA2";
+ protocol.add(ScanResult.PROTOCOL_WPA2);
+
+ // group data cipher suite
+ groupCipher.add(parseRsnCipher(buf.getInt()));
// pairwise cipher suite count
short cipherCount = buf.getShort();
-
+ ArrayList<Integer> rsnPairwiseCipher = new ArrayList<>();
// pairwise cipher suite list
for (int i = 0; i < cipherCount; i++) {
- // here we simply advance the buffer position
- buf.getInt();
+ rsnPairwiseCipher.add(parseRsnCipher(buf.getInt()));
}
+ pairwiseCipher.add(rsnPairwiseCipher);
// AKM
// AKM suite count
short akmCount = buf.getShort();
+ ArrayList<Integer> rsnKeyManagement = new ArrayList<>();
- // parse AKM suite list
- if (akmCount == 0) {
- security += "-EAP"; //default AKM
- }
- boolean found = false;
for (int i = 0; i < akmCount; i++) {
int akm = buf.getInt();
switch (akm) {
case WPA2_AKM_EAP:
- security += (found ? "+" : "-") + "EAP";
- found = true;
+ rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
break;
case WPA2_AKM_PSK:
- security += (found ? "+" : "-") + "PSK";
- found = true;
+ rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK);
break;
case WPA2_AKM_FT_EAP:
- security += (found ? "+" : "-") + "FT/EAP";
- found = true;
+ rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_EAP);
break;
case WPA2_AKM_FT_PSK:
- security += (found ? "+" : "-") + "FT/PSK";
- found = true;
+ rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK);
break;
case WPA2_AKM_EAP_SHA256:
- security += (found ? "+" : "-") + "EAP-SHA256";
- found = true;
+ rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_SHA256);
break;
case WPA2_AKM_PSK_SHA256:
- security += (found ? "+" : "-") + "PSK-SHA256";
- found = true;
+ rsnKeyManagement.add(ScanResult.KEY_MGMT_PSK_SHA256);
break;
default:
// do nothing
break;
}
}
-
- // we parsed what we want at this point
- security += "]";
- return security;
+ // Default AKM
+ if (rsnKeyManagement.isEmpty()) {
+ rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP);
+ }
+ keyManagement.add(rsnKeyManagement);
} catch (BufferUnderflowException e) {
Log.e("IE_Capabilities", "Couldn't parse RSNE, buffer underflow");
- return null;
+ }
+ }
+
+ private static int parseWpaCipher(int cipher) {
+ switch (cipher) {
+ case WPA_CIPHER_NONE:
+ return ScanResult.CIPHER_NONE;
+ case WPA_CIPHER_TKIP:
+ return ScanResult.CIPHER_TKIP;
+ case WPA_CIPHER_CCMP:
+ return ScanResult.CIPHER_CCMP;
+ default:
+ Log.w("IE_Capabilities", "Unknown WPA cipher suite: "
+ + Integer.toHexString(cipher));
+ return ScanResult.CIPHER_NONE;
+ }
+ }
+
+ private static int parseRsnCipher(int cipher) {
+ switch (cipher) {
+ case RSN_CIPHER_NONE:
+ return ScanResult.CIPHER_NONE;
+ case RSN_CIPHER_TKIP:
+ return ScanResult.CIPHER_TKIP;
+ case RSN_CIPHER_CCMP:
+ return ScanResult.CIPHER_CCMP;
+ case RSN_CIPHER_NO_GROUP_ADDRESSED:
+ return ScanResult.CIPHER_NO_GROUP_ADDRESSED;
+ default:
+ Log.w("IE_Capabilities", "Unknown RSN cipher suite: "
+ + Integer.toHexString(cipher));
+ return ScanResult.CIPHER_NONE;
+ }
+ }
+
+ private static boolean isWpsElement(InformationElement ie) {
+ ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
+ try {
+ // WPS OUI and type
+ return (buf.getInt() == WPS_VENDOR_OUI_TYPE);
+ } catch (BufferUnderflowException e) {
+ Log.e("IE_Capabilities", "Couldn't parse VSA IE, buffer underflow");
+ return false;
}
}
@@ -437,6 +561,8 @@
//
// | Element ID | Length | OUI | Type | Version |
// 1 1 3 1 2
+ // | Group Data Cipher Suite |
+ // 4
// | Pairwise Cipher Suite Count | Pairwise Cipher Suite List |
// 2 4 * m
// | AKM Suite Count | AKM Suite List |
@@ -445,7 +571,7 @@
// Note: InformationElement.bytes has 'Element ID' and 'Length'
// stripped off already
//
- private static String parseWpaOneElement(InformationElement ie) {
+ private void parseWpaOneElement(InformationElement ie) {
ByteBuffer buf = ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN);
try {
@@ -453,105 +579,175 @@
// been called for verification before we reach here.
buf.getInt();
- // start building the string
- String security = "[WPA";
-
// version
if (buf.getShort() != WPA_VENDOR_OUI_VERSION) {
// incorrect version
- return null;
+ return;
}
+ // start building the string
+ protocol.add(ScanResult.PROTOCOL_WPA);
+
// group data cipher suite
- // here we simply advance buffer position
- buf.getInt();
+ groupCipher.add(parseWpaCipher(buf.getInt()));
// pairwise cipher suite count
short cipherCount = buf.getShort();
-
+ ArrayList<Integer> wpaPairwiseCipher = new ArrayList<>();
// pairwise chipher suite list
for (int i = 0; i < cipherCount; i++) {
- // here we simply advance buffer position
- buf.getInt();
+ wpaPairwiseCipher.add(parseWpaCipher(buf.getInt()));
}
+ pairwiseCipher.add(wpaPairwiseCipher);
// AKM
// AKM suite count
short akmCount = buf.getShort();
+ ArrayList<Integer> wpaKeyManagement = new ArrayList<>();
// AKM suite list
- if (akmCount == 0) {
- security += "-EAP"; //default AKM
- }
- boolean found = false;
for (int i = 0; i < akmCount; i++) {
int akm = buf.getInt();
switch (akm) {
case WPA_AKM_EAP:
- security += (found ? "+" : "-") + "EAP";
- found = true;
+ wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
break;
case WPA_AKM_PSK:
- security += (found ? "+" : "-") + "PSK";
- found = true;
+ wpaKeyManagement.add(ScanResult.KEY_MGMT_PSK);
break;
default:
// do nothing
break;
}
}
-
- // we parsed what we want at this point
- security += "]";
- return security;
+ // Default AKM
+ if (wpaKeyManagement.isEmpty()) {
+ wpaKeyManagement.add(ScanResult.KEY_MGMT_EAP);
+ }
+ keyManagement.add(wpaKeyManagement);
} catch (BufferUnderflowException e) {
Log.e("IE_Capabilities", "Couldn't parse type 1 WPA, buffer underflow");
- return null;
}
}
/**
* Parse the Information Element and the 16-bit Capability Information field
- * to build the ScanResult.capabilities String.
+ * to build the InformationElemmentUtil.capabilities object.
*
* @param ies -- Information Element array
* @param beaconCap -- 16-bit Beacon Capability Information field
- * @return security string that mirrors what wpa_supplicant generates
*/
- public static String buildCapabilities(InformationElement[] ies, BitSet beaconCap) {
- String capabilities = "";
- boolean rsneFound = false;
- boolean wpaFound = false;
+
+ public void from(InformationElement[] ies, BitSet beaconCap) {
+ protocol = new ArrayList<Integer>();
+ keyManagement = new ArrayList<ArrayList<Integer>>();
+ groupCipher = new ArrayList<Integer>();
+ pairwiseCipher = new ArrayList<ArrayList<Integer>>();
if (ies == null || beaconCap == null) {
- return capabilities;
+ return;
}
-
- boolean ess = beaconCap.get(CAP_ESS_BIT_OFFSET);
- boolean privacy = beaconCap.get(CAP_PRIVACY_BIT_OFFSET);
-
+ isESS = beaconCap.get(CAP_ESS_BIT_OFFSET);
+ isPrivacy = beaconCap.get(CAP_PRIVACY_BIT_OFFSET);
for (InformationElement ie : ies) {
if (ie.id == InformationElement.EID_RSN) {
- rsneFound = true;
- capabilities += parseRsnElement(ie);
+ parseRsnElement(ie);
}
if (ie.id == InformationElement.EID_VSA) {
if (isWpaOneElement(ie)) {
- wpaFound = true;
- capabilities += parseWpaOneElement(ie);
+ parseWpaOneElement(ie);
+ }
+ if (isWpsElement(ie)) {
+ // TODO(b/62134557): parse WPS IE to provide finer granularity information.
+ isWPS = true;
}
}
}
+ }
- if (!rsneFound && !wpaFound && privacy) {
- //private Beacon without an RSNE or WPA IE, hence WEP0
+ private String protocolToString(int protocol) {
+ switch (protocol) {
+ case ScanResult.PROTOCOL_NONE:
+ return "None";
+ case ScanResult.PROTOCOL_WPA:
+ return "WPA";
+ case ScanResult.PROTOCOL_WPA2:
+ return "WPA2";
+ default:
+ return "?";
+ }
+ }
+
+ private String keyManagementToString(int akm) {
+ switch (akm) {
+ case ScanResult.KEY_MGMT_NONE:
+ return "None";
+ case ScanResult.KEY_MGMT_PSK:
+ return "PSK";
+ case ScanResult.KEY_MGMT_EAP:
+ return "EAP";
+ case ScanResult.KEY_MGMT_FT_EAP:
+ return "FT/EAP";
+ case ScanResult.KEY_MGMT_FT_PSK:
+ return "FT/PSK";
+ case ScanResult.KEY_MGMT_EAP_SHA256:
+ return "EAP-SHA256";
+ case ScanResult.KEY_MGMT_PSK_SHA256:
+ return "PSK-SHA256";
+ default:
+ return "?";
+ }
+ }
+
+ private String cipherToString(int cipher) {
+ switch (cipher) {
+ case ScanResult.CIPHER_NONE:
+ return "None";
+ case ScanResult.CIPHER_CCMP:
+ return "CCMP";
+ case ScanResult.CIPHER_TKIP:
+ return "TKIP";
+ default:
+ return "?";
+ }
+ }
+
+ /**
+ * Build the ScanResult.capabilities String.
+ *
+ * @return security string that mirrors what wpa_supplicant generates
+ */
+ public String generateCapabilitiesString() {
+ String capabilities = "";
+ // private Beacon without an RSNE or WPA IE, hence WEP0
+ boolean isWEP = (protocol.isEmpty()) && isPrivacy;
+
+ if (isWEP) {
capabilities += "[WEP]";
}
-
- if (ess) {
+ for (int i = 0; i < protocol.size(); i++) {
+ capabilities += "[" + protocolToString(protocol.get(i));
+ if (i < keyManagement.size()) {
+ for (int j = 0; j < keyManagement.get(i).size(); j++) {
+ capabilities += ((j == 0) ? "-" : "+")
+ + keyManagementToString(keyManagement.get(i).get(j));
+ }
+ }
+ if (i < pairwiseCipher.size()) {
+ for (int j = 0; j < pairwiseCipher.get(i).size(); j++) {
+ capabilities += ((j == 0) ? "-" : "+")
+ + cipherToString(pairwiseCipher.get(i).get(j));
+ }
+ }
+ capabilities += "]";
+ }
+ if (isESS) {
capabilities += "[ESS]";
}
+ if (isWPS) {
+ capabilities += "[WPS]";
+ }
return capabilities;
}
diff --git a/service/java/com/android/server/wifi/util/NativeUtil.java b/service/java/com/android/server/wifi/util/NativeUtil.java
new file mode 100644
index 0000000..07c3f9b
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/NativeUtil.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.util;
+
+import android.text.TextUtils;
+
+import com.android.server.wifi.ByteBufferReader;
+
+import libcore.util.HexEncoding;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+/**
+ * Provide utility functions for native interfacing modules.
+ */
+public class NativeUtil {
+ private static final String ANY_MAC_STR = "any";
+ public static final byte[] ANY_MAC_BYTES = {0, 0, 0, 0, 0, 0};
+ private static final int MAC_LENGTH = 6;
+ private static final int MAC_OUI_LENGTH = 3;
+ private static final int MAC_STR_LENGTH = MAC_LENGTH * 2 + 5;
+
+ /**
+ * Convert the string to byte array list.
+ *
+ * @return the UTF_8 char byte values of str, as an ArrayList.
+ * @throws IllegalArgumentException if a null string is sent.
+ */
+ public static ArrayList<Byte> stringToByteArrayList(String str) {
+ if (str == null) {
+ throw new IllegalArgumentException("null string");
+ }
+ ArrayList<Byte> byteArrayList = new ArrayList<Byte>();
+ for (byte b : str.getBytes(StandardCharsets.UTF_8)) {
+ byteArrayList.add(new Byte(b));
+ }
+ return byteArrayList;
+ }
+
+ /**
+ * Convert the byte array list to string.
+ *
+ * @return the string decoded from UTF_8 byte values in byteArrayList.
+ * @throws IllegalArgumentException if a null byte array list is sent.
+ */
+ public static String stringFromByteArrayList(ArrayList<Byte> byteArrayList) {
+ if (byteArrayList == null) {
+ throw new IllegalArgumentException("null byte array list");
+ }
+ byte[] byteArray = new byte[byteArrayList.size()];
+ int i = 0;
+ for (Byte b : byteArrayList) {
+ byteArray[i] = b;
+ i++;
+ }
+ return new String(byteArray, StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Convert the string to byte array.
+ *
+ * @return the UTF_8 char byte values of str, as an Array.
+ * @throws IllegalArgumentException if a null string is sent.
+ */
+ public static byte[] stringToByteArray(String str) {
+ if (str == null) {
+ throw new IllegalArgumentException("null string");
+ }
+ return str.getBytes(StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Convert the byte array list to string.
+ *
+ * @return the string decoded from UTF_8 byte values in byteArray.
+ * @throws IllegalArgumentException if a null byte array is sent.
+ */
+ public static String stringFromByteArray(byte[] byteArray) {
+ if (byteArray == null) {
+ throw new IllegalArgumentException("null byte array");
+ }
+ return new String(byteArray);
+ }
+
+ /**
+ * Converts a mac address string to an array of Bytes.
+ *
+ * @param macStr string of format: "XX:XX:XX:XX:XX:XX" or "XXXXXXXXXXXX", where X is any
+ * hexadecimal digit.
+ * Passing null, empty string or "any" is the same as 00:00:00:00:00:00
+ * @throws IllegalArgumentException for various malformed inputs.
+ */
+ public static byte[] macAddressToByteArray(String macStr) {
+ if (TextUtils.isEmpty(macStr) || ANY_MAC_STR.equals(macStr)) return ANY_MAC_BYTES;
+ String cleanMac = macStr.replace(":", "");
+ if (cleanMac.length() != MAC_LENGTH * 2) {
+ throw new IllegalArgumentException("invalid mac string length: " + cleanMac);
+ }
+ return HexEncoding.decode(cleanMac.toCharArray(), false);
+ }
+
+ /**
+ * Converts an array of 6 bytes to a HexEncoded String with format: "XX:XX:XX:XX:XX:XX", where X
+ * is any hexadecimal digit.
+ *
+ * @param macArray byte array of mac values, must have length 6
+ * @throws IllegalArgumentException for malformed inputs.
+ */
+ public static String macAddressFromByteArray(byte[] macArray) {
+ if (macArray == null) {
+ throw new IllegalArgumentException("null mac bytes");
+ }
+ if (macArray.length != MAC_LENGTH) {
+ throw new IllegalArgumentException("invalid macArray length: " + macArray.length);
+ }
+ StringBuilder sb = new StringBuilder(MAC_STR_LENGTH);
+ for (int i = 0; i < macArray.length; i++) {
+ if (i != 0) sb.append(":");
+ sb.append(new String(HexEncoding.encode(macArray, i, 1)));
+ }
+ return sb.toString().toLowerCase();
+ }
+
+ /**
+ * Converts a mac address OUI string to an array of Bytes.
+ *
+ * @param macStr string of format: "XX:XX:XX" or "XXXXXX", where X is any hexadecimal digit.
+ * @throws IllegalArgumentException for various malformed inputs.
+ */
+ public static byte[] macAddressOuiToByteArray(String macStr) {
+ if (macStr == null) {
+ throw new IllegalArgumentException("null mac string");
+ }
+ String cleanMac = macStr.replace(":", "");
+ if (cleanMac.length() != MAC_OUI_LENGTH * 2) {
+ throw new IllegalArgumentException("invalid mac oui string length: " + cleanMac);
+ }
+ return HexEncoding.decode(cleanMac.toCharArray(), false);
+ }
+
+ /**
+ * Converts an array of 6 bytes to a long representing the MAC address.
+ *
+ * @param macArray byte array of mac values, must have length 6
+ * @return Long value of the mac address.
+ * @throws IllegalArgumentException for malformed inputs.
+ */
+ public static Long macAddressToLong(byte[] macArray) {
+ if (macArray == null) {
+ throw new IllegalArgumentException("null mac bytes");
+ }
+ if (macArray.length != MAC_LENGTH) {
+ throw new IllegalArgumentException("invalid macArray length: " + macArray.length);
+ }
+ try {
+ return ByteBufferReader.readInteger(
+ ByteBuffer.wrap(macArray), ByteOrder.BIG_ENDIAN, macArray.length);
+ } catch (BufferUnderflowException | IllegalArgumentException e) {
+ throw new IllegalArgumentException("invalid macArray");
+ }
+ }
+
+ /**
+ * Remove enclosed quotes of the provided string.
+ *
+ * @param quotedStr String to be unquoted.
+ * @return String without the enclosing quotes.
+ */
+ public static String removeEnclosingQuotes(String quotedStr) {
+ int length = quotedStr.length();
+ if ((length >= 2)
+ && (quotedStr.charAt(0) == '"') && (quotedStr.charAt(length - 1) == '"')) {
+ return quotedStr.substring(1, length - 1);
+ }
+ return quotedStr;
+ }
+
+ /**
+ * Add enclosing quotes of the provided string.
+ *
+ * @param str String to be uoted.
+ * @return String with the enclosing quotes.
+ */
+ public static String addEnclosingQuotes(String str) {
+ return "\"" + str + "\"";
+ }
+
+ /**
+ * Converts an string to an arraylist of UTF_8 byte values.
+ * These forms are acceptable:
+ * a) ASCII String encapsulated in quotes, or
+ * b) Hex string with no delimiters.
+ *
+ * @param str String to be converted.
+ * @throws IllegalArgumentException for null string.
+ */
+ public static ArrayList<Byte> hexOrQuotedAsciiStringToBytes(String str) {
+ if (str == null) {
+ throw new IllegalArgumentException("null string");
+ }
+ int length = str.length();
+ if ((length > 1) && (str.charAt(0) == '"') && (str.charAt(length - 1) == '"')) {
+ str = str.substring(1, str.length() - 1);
+ return stringToByteArrayList(str);
+ } else {
+ return byteArrayToArrayList(hexStringToByteArray(str));
+ }
+ }
+
+ /**
+ * Converts an ArrayList<Byte> of UTF_8 byte values to string.
+ * The string will either be:
+ * a) ASCII String encapsulated in quotes (if all the bytes are ASCII encodeable and non null),
+ * or
+ * b) Hex string with no delimiters.
+ *
+ * @param bytes List of bytes for ssid.
+ * @throws IllegalArgumentException for null bytes.
+ */
+ public static String bytesToHexOrQuotedAsciiString(ArrayList<Byte> bytes) {
+ if (bytes == null) {
+ throw new IllegalArgumentException("null ssid bytes");
+ }
+ byte[] byteArray = byteArrayFromArrayList(bytes);
+ // Check for 0's in the byte stream in which case we cannot convert this into a string.
+ if (!bytes.contains(Byte.valueOf((byte) 0))) {
+ CharsetDecoder decoder = StandardCharsets.US_ASCII.newDecoder();
+ try {
+ CharBuffer decoded = decoder.decode(ByteBuffer.wrap(byteArray));
+ return "\"" + decoded.toString() + "\"";
+ } catch (CharacterCodingException cce) {
+ }
+ }
+ return hexStringFromByteArray(byteArray);
+ }
+
+ /**
+ * Converts an ssid string to an arraylist of UTF_8 byte values.
+ * These forms are acceptable:
+ * a) ASCII String encapsulated in quotes, or
+ * b) Hex string with no delimiters.
+ *
+ * @param ssidStr String to be converted.
+ * @throws IllegalArgumentException for null string.
+ */
+ public static ArrayList<Byte> decodeSsid(String ssidStr) {
+ return hexOrQuotedAsciiStringToBytes(ssidStr);
+ }
+
+ /**
+ * Converts an ArrayList<Byte> of UTF_8 byte values to ssid string.
+ * The string will either be:
+ * a) ASCII String encapsulated in quotes (if all the bytes are ASCII encodeable and non null),
+ * or
+ * b) Hex string with no delimiters.
+ *
+ * @param ssidBytes List of bytes for ssid.
+ * @throws IllegalArgumentException for null bytes.
+ */
+ public static String encodeSsid(ArrayList<Byte> ssidBytes) {
+ return bytesToHexOrQuotedAsciiString(ssidBytes);
+ }
+
+ /**
+ * Convert from an array of primitive bytes to an array list of Byte.
+ */
+ public static ArrayList<Byte> byteArrayToArrayList(byte[] bytes) {
+ ArrayList<Byte> byteList = new ArrayList<>();
+ for (Byte b : bytes) {
+ byteList.add(b);
+ }
+ return byteList;
+ }
+
+ /**
+ * Convert from an array list of Byte to an array of primitive bytes.
+ */
+ public static byte[] byteArrayFromArrayList(ArrayList<Byte> bytes) {
+ byte[] byteArray = new byte[bytes.size()];
+ int i = 0;
+ for (Byte b : bytes) {
+ byteArray[i++] = b;
+ }
+ return byteArray;
+ }
+
+ /**
+ * Converts a hex string to byte array.
+ *
+ * @param hexStr String to be converted.
+ * @throws IllegalArgumentException for null string.
+ */
+ public static byte[] hexStringToByteArray(String hexStr) {
+ if (hexStr == null) {
+ throw new IllegalArgumentException("null hex string");
+ }
+ return HexEncoding.decode(hexStr.toCharArray(), false);
+ }
+
+ /**
+ * Converts a byte array to hex string.
+ *
+ * @param bytes List of bytes for ssid.
+ * @throws IllegalArgumentException for null bytes.
+ */
+ public static String hexStringFromByteArray(byte[] bytes) {
+ if (bytes == null) {
+ throw new IllegalArgumentException("null hex bytes");
+ }
+ return new String(HexEncoding.encode(bytes)).toLowerCase();
+ }
+}
diff --git a/service/java/com/android/server/wifi/util/OWNERS b/service/java/com/android/server/wifi/util/OWNERS
new file mode 100644
index 0000000..9e0a7ec
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/OWNERS
@@ -0,0 +1,12 @@
+# diagnostics
+per-file ByteArrayRingBuffer*=quiche@google.com
+per-file FrameParser*=quiche@google.com
+
+# random bits
+per-file StringUtil*=quiche@google.com
+per-file WifiPermissionsUtil*=sohanirao@google.com
+per-file WifiPermissionsWrapper*=sohanirao@google.com
+
+# tracing (app/framework interaction)
+per-file WifiAsyncChannel*=sohanirao@google.com
+per-file WifiHandler*=sohanirao@google.com
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 c5ec92a..0000000
--- a/service/java/com/android/server/wifi/util/ScanDetailUtil.java
+++ /dev/null
@@ -1,48 +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);
- }
-
- /**
- * Helper method to quote the SSID in Scan result to use for comparing/filling SSID stored in
- * WifiConfiguration object.
- */
- public static String createQuotedSSID(String ssid) {
- return "\"" + ssid + "\"";
- }
-}
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..0e08701
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/ScanResultUtil.java
@@ -0,0 +1,150 @@
+/*
+ * 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 android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.WifiConfigurationUtil;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+
+/**
+ * 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);
+ }
+
+ /**
+ * 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 quote the SSID in Scan result to use for comparing/filling SSID stored in
+ * WifiConfiguration object.
+ */
+ @VisibleForTesting
+ public static String createQuotedSSID(String ssid) {
+ return "\"" + ssid + "\"";
+ }
+
+ /**
+ * Checks if the provided |scanResult| match with the provided |config|. Essentially checks
+ * if the network config and scan result have the same SSID and encryption type.
+ */
+ public static boolean doesScanResultMatchWithNetwork(
+ ScanResult scanResult, WifiConfiguration config) {
+ // Add the double quotes to the scan result SSID for comparison with the network configs.
+ String configSSID = createQuotedSSID(scanResult.SSID);
+ if (TextUtils.equals(config.SSID, configSSID)) {
+ if (ScanResultUtil.isScanResultForPskNetwork(scanResult)
+ && WifiConfigurationUtil.isConfigForPskNetwork(config)) {
+ return true;
+ }
+ if (ScanResultUtil.isScanResultForEapNetwork(scanResult)
+ && WifiConfigurationUtil.isConfigForEapNetwork(config)) {
+ return true;
+ }
+ if (ScanResultUtil.isScanResultForWepNetwork(scanResult)
+ && WifiConfigurationUtil.isConfigForWepNetwork(config)) {
+ return true;
+ }
+ if (ScanResultUtil.isScanResultForOpenNetwork(scanResult)
+ && WifiConfigurationUtil.isConfigForOpenNetwork(config)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Creates a network configuration object using the provided |scanResult|.
+ * This is used to create ephemeral network configurations.
+ */
+ public static WifiConfiguration createNetworkFromScanResult(ScanResult scanResult) {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = createQuotedSSID(scanResult.SSID);
+ setAllowedKeyManagementFromScanResult(scanResult, config);
+ return config;
+ }
+
+ /**
+ * Sets the {@link WifiConfiguration#allowedKeyManagement} field on the given
+ * {@link WifiConfiguration} based on its corresponding {@link ScanResult}.
+ */
+ public static void setAllowedKeyManagementFromScanResult(ScanResult scanResult,
+ WifiConfiguration config) {
+ if (isScanResultForPskNetwork(scanResult)) {
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ } else if (isScanResultForEapNetwork(scanResult)) {
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ } else if (isScanResultForWepNetwork(scanResult)) {
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
+ config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
+ } else {
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ }
+ }
+}
diff --git a/service/java/com/android/server/wifi/util/TelephonyUtil.java b/service/java/com/android/server/wifi/util/TelephonyUtil.java
index 3d7ce45..5c489b8 100644
--- a/service/java/com/android/server/wifi/util/TelephonyUtil.java
+++ b/service/java/com/android/server/wifi/util/TelephonyUtil.java
@@ -16,33 +16,40 @@
package com.android.server.wifi.util;
-import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.telephony.TelephonyManager;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.server.wifi.WifiNative;
/**
* Utilities for the Wifi Service to interact with telephony.
*/
public class TelephonyUtil {
+ public static final String TAG = "TelephonyUtil";
/**
- * Get the identity for the current SIM or null if the sim is not available
+ * Get the identity for the current SIM or null if the SIM is not available
+ *
+ * @param tm TelephonyManager instance
+ * @param config WifiConfiguration that indicates what sort of authentication is necessary
+ * @return String with the identity or none if the SIM is not available or config is invalid
*/
- public static String getSimIdentity(Context context, int eapMethod) {
- TelephonyManager tm = TelephonyManager.from(context);
- if (tm != null) {
- String imsi = tm.getSubscriberId();
- String mccMnc = "";
-
- if (tm.getSimState() == TelephonyManager.SIM_STATE_READY) {
- mccMnc = tm.getSimOperator();
- }
-
- return buildIdentity(eapMethod, imsi, mccMnc);
- } else {
+ public static String getSimIdentity(TelephonyManager tm, WifiConfiguration config) {
+ if (tm == null) {
+ Log.e(TAG, "No valid TelephonyManager");
return null;
}
+ String imsi = tm.getSubscriberId();
+ String mccMnc = "";
+
+ if (tm.getSimState() == TelephonyManager.SIM_STATE_READY) {
+ mccMnc = tm.getSimOperator();
+ }
+
+ return buildIdentity(getSimMethodForConfig(config), imsi, mccMnc);
}
/**
@@ -55,6 +62,7 @@
*/
private static String buildIdentity(int eapMethod, String imsi, String mccMnc) {
if (imsi == null || imsi.isEmpty()) {
+ Log.e(TAG, "No IMSI or IMSI is null");
return null;
}
@@ -65,7 +73,8 @@
prefix = "0";
} else if (eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME) {
prefix = "6";
- } else { // not a valide EapMethod
+ } else {
+ Log.e(TAG, "Invalid EAP method");
return null;
}
@@ -88,28 +97,293 @@
}
/**
- * Checks if the network is a sim config.
+ * Return the associated SIM method for the configuration.
*
- * @param config Config corresponding to the network.
- * @return true if it is a sim config, false otherwise.
+ * @param config WifiConfiguration corresponding to the network.
+ * @return the outer EAP method associated with this SIM configuration.
*/
- public static boolean isSimConfig(WifiConfiguration config) {
+ private static int getSimMethodForConfig(WifiConfiguration config) {
if (config == null || config.enterpriseConfig == null) {
- return false;
+ return WifiEnterpriseConfig.Eap.NONE;
+ }
+ int eapMethod = config.enterpriseConfig.getEapMethod();
+ if (eapMethod == WifiEnterpriseConfig.Eap.PEAP) {
+ // Translate known inner eap methods into an equivalent outer eap method.
+ switch (config.enterpriseConfig.getPhase2Method()) {
+ case WifiEnterpriseConfig.Phase2.SIM:
+ eapMethod = WifiEnterpriseConfig.Eap.SIM;
+ break;
+ case WifiEnterpriseConfig.Phase2.AKA:
+ eapMethod = WifiEnterpriseConfig.Eap.AKA;
+ break;
+ case WifiEnterpriseConfig.Phase2.AKA_PRIME:
+ eapMethod = WifiEnterpriseConfig.Eap.AKA_PRIME;
+ break;
+ }
}
- return isSimEapMethod(config.enterpriseConfig.getEapMethod());
+ return isSimEapMethod(eapMethod) ? eapMethod : WifiEnterpriseConfig.Eap.NONE;
}
/**
- * Checks if the network is a sim config.
+ * Checks if the network is a SIM config.
*
- * @param method
- * @return true if it is a sim config, false otherwise.
+ * @param config Config corresponding to the network.
+ * @return true if it is a SIM config, false otherwise.
+ */
+ public static boolean isSimConfig(WifiConfiguration config) {
+ return getSimMethodForConfig(config) != WifiEnterpriseConfig.Eap.NONE;
+ }
+
+ /**
+ * Checks if the EAP outer method is SIM related.
+ *
+ * @param eapMethod WifiEnterpriseConfig Eap method.
+ * @return true if this EAP outer method is SIM-related, false otherwise.
*/
public static boolean isSimEapMethod(int eapMethod) {
return eapMethod == WifiEnterpriseConfig.Eap.SIM
|| eapMethod == WifiEnterpriseConfig.Eap.AKA
|| eapMethod == WifiEnterpriseConfig.Eap.AKA_PRIME;
}
+
+ // TODO replace some of this code with Byte.parseByte
+ private static int parseHex(char ch) {
+ if ('0' <= ch && ch <= '9') {
+ return ch - '0';
+ } else if ('a' <= ch && ch <= 'f') {
+ return ch - 'a' + 10;
+ } else if ('A' <= ch && ch <= 'F') {
+ return ch - 'A' + 10;
+ } else {
+ throw new NumberFormatException("" + ch + " is not a valid hex digit");
+ }
+ }
+
+ private static byte[] parseHex(String hex) {
+ /* This only works for good input; don't throw bad data at it */
+ if (hex == null) {
+ return new byte[0];
+ }
+
+ if (hex.length() % 2 != 0) {
+ throw new NumberFormatException(hex + " is not a valid hex string");
+ }
+
+ byte[] result = new byte[(hex.length()) / 2 + 1];
+ result[0] = (byte) ((hex.length()) / 2);
+ for (int i = 0, j = 1; i < hex.length(); i += 2, j++) {
+ int val = parseHex(hex.charAt(i)) * 16 + parseHex(hex.charAt(i + 1));
+ byte b = (byte) (val & 0xFF);
+ result[j] = b;
+ }
+
+ return result;
+ }
+
+ private static String makeHex(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(String.format("%02x", b));
+ }
+ return sb.toString();
+ }
+
+ private static String makeHex(byte[] bytes, int from, int len) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < len; i++) {
+ sb.append(String.format("%02x", bytes[from + i]));
+ }
+ return sb.toString();
+ }
+
+ private static byte[] concatHex(byte[] array1, byte[] array2) {
+
+ int len = array1.length + array2.length;
+
+ byte[] result = new byte[len];
+
+ int index = 0;
+ if (array1.length != 0) {
+ for (byte b : array1) {
+ result[index] = b;
+ index++;
+ }
+ }
+
+ if (array2.length != 0) {
+ for (byte b : array2) {
+ result[index] = b;
+ index++;
+ }
+ }
+
+ return result;
+ }
+
+ public static String getGsmSimAuthResponse(String[] requestData, TelephonyManager tm) {
+ if (tm == null) {
+ Log.e(TAG, "No valid TelephonyManager");
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (String challenge : requestData) {
+ if (challenge == null || challenge.isEmpty()) {
+ continue;
+ }
+ Log.d(TAG, "RAND = " + challenge);
+
+ byte[] rand = null;
+ try {
+ rand = parseHex(challenge);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "malformed challenge");
+ continue;
+ }
+
+ String base64Challenge = Base64.encodeToString(rand, Base64.NO_WRAP);
+
+ // Try USIM first for authentication.
+ 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(TelephonyManager.APPTYPE_SIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge);
+ }
+ Log.v(TAG, "Raw Response - " + tmResponse);
+
+ if (tmResponse == null || tmResponse.length() <= 4) {
+ Log.e(TAG, "bad response - " + tmResponse);
+ return null;
+ }
+
+ byte[] result = Base64.decode(tmResponse, Base64.DEFAULT);
+ Log.v(TAG, "Hex Response -" + makeHex(result));
+ int sresLen = result[0];
+ if (sresLen >= result.length) {
+ Log.e(TAG, "malfomed response - " + tmResponse);
+ return null;
+ }
+ String sres = makeHex(result, 1, sresLen);
+ int kcOffset = 1 + sresLen;
+ if (kcOffset >= result.length) {
+ Log.e(TAG, "malfomed response - " + tmResponse);
+ return null;
+ }
+ int kcLen = result[kcOffset];
+ if (kcOffset + kcLen > result.length) {
+ Log.e(TAG, "malfomed response - " + tmResponse);
+ return null;
+ }
+ String kc = makeHex(result, 1 + kcOffset, kcLen);
+ sb.append(":" + kc + ":" + sres);
+ Log.v(TAG, "kc:" + kc + " sres:" + sres);
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Data supplied when making a SIM Auth Request
+ */
+ public static class SimAuthRequestData {
+ public SimAuthRequestData() {}
+ public SimAuthRequestData(int networkId, int protocol, String ssid, String[] data) {
+ this.networkId = networkId;
+ this.protocol = protocol;
+ this.ssid = ssid;
+ this.data = data;
+ }
+
+ public int networkId;
+ public int protocol;
+ public String ssid;
+ // EAP-SIM: data[] contains the 3 rand, one for each of the 3 challenges
+ // EAP-AKA/AKA': data[] contains rand & authn couple for the single challenge
+ public String[] data;
+ }
+
+ /**
+ * The response to a SIM Auth request if successful
+ */
+ public static class SimAuthResponseData {
+ public SimAuthResponseData(String type, String response) {
+ this.type = type;
+ this.response = response;
+ }
+
+ public String type;
+ public String response;
+ }
+
+ public static SimAuthResponseData get3GAuthResponse(SimAuthRequestData requestData,
+ TelephonyManager tm) {
+ StringBuilder sb = new StringBuilder();
+ byte[] rand = null;
+ byte[] authn = null;
+ String resType = WifiNative.SIM_AUTH_RESP_TYPE_UMTS_AUTH;
+
+ if (requestData.data.length == 2) {
+ try {
+ rand = parseHex(requestData.data[0]);
+ authn = parseHex(requestData.data[1]);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "malformed challenge");
+ }
+ } else {
+ Log.e(TAG, "malformed challenge");
+ }
+
+ String tmResponse = "";
+ if (rand != null && authn != null) {
+ String base64Challenge = Base64.encodeToString(concatHex(rand, authn), Base64.NO_WRAP);
+ if (tm != null) {
+ tmResponse = tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_AKA, base64Challenge);
+ Log.v(TAG, "Raw Response - " + tmResponse);
+ } else {
+ Log.e(TAG, "No valid TelephonyManager");
+ }
+ }
+
+ boolean goodReponse = false;
+ if (tmResponse != null && tmResponse.length() > 4) {
+ byte[] result = Base64.decode(tmResponse, Base64.DEFAULT);
+ Log.e(TAG, "Hex Response - " + makeHex(result));
+ byte tag = result[0];
+ if (tag == (byte) 0xdb) {
+ Log.v(TAG, "successful 3G authentication ");
+ int resLen = result[1];
+ String res = makeHex(result, 2, resLen);
+ int ckLen = result[resLen + 2];
+ String ck = makeHex(result, resLen + 3, ckLen);
+ int ikLen = result[resLen + ckLen + 3];
+ String ik = makeHex(result, resLen + ckLen + 4, ikLen);
+ sb.append(":" + ik + ":" + ck + ":" + res);
+ Log.v(TAG, "ik:" + ik + "ck:" + ck + " res:" + res);
+ goodReponse = true;
+ } else if (tag == (byte) 0xdc) {
+ Log.e(TAG, "synchronisation failure");
+ int autsLen = result[1];
+ String auts = makeHex(result, 2, autsLen);
+ resType = WifiNative.SIM_AUTH_RESP_TYPE_UMTS_AUTS;
+ sb.append(":" + auts);
+ Log.v(TAG, "auts:" + auts);
+ goodReponse = true;
+ } else {
+ Log.e(TAG, "bad response - unknown tag = " + tag);
+ }
+ } else {
+ Log.e(TAG, "bad response - " + tmResponse);
+ }
+
+ if (goodReponse) {
+ String response = sb.toString();
+ Log.v(TAG, "Supplicant Response -" + response);
+ return new SimAuthResponseData(resType, response);
+ } else {
+ return null;
+ }
+ }
}
diff --git a/service/java/com/android/server/wifi/util/WifiAsyncChannel.java b/service/java/com/android/server/wifi/util/WifiAsyncChannel.java
new file mode 100644
index 0000000..08025e3
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/WifiAsyncChannel.java
@@ -0,0 +1,104 @@
+/*
+ * 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.annotation.NonNull;
+import android.os.Message;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.AsyncChannel;
+import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.WifiLog;
+
+/**
+ * This class subclasses AsyncChannel and adds logging
+ * to the sendMessage() API
+ */
+public class WifiAsyncChannel extends AsyncChannel {
+ private static final String LOG_TAG = "WifiAsyncChannel";
+ private WifiLog mLog;
+ private String mTag;
+ /**
+ * AsyncChannelWithLogging constructor
+ */
+ public WifiAsyncChannel(String serviceTag) {
+ mTag = LOG_TAG + "." + serviceTag;
+ }
+
+ @NonNull
+ private WifiLog getOrInitLog() {
+ // Lazy initization of mLog
+ if (mLog == null) {
+ mLog = WifiInjector.getInstance().makeLog(mTag);
+ }
+ return mLog;
+ }
+
+ /**
+ * Send a message to the destination handler.
+ *
+ * @param msg
+ */
+ @Override
+ public void sendMessage(Message msg) {
+ getOrInitLog().trace("sendMessage message=%")
+ .c(msg.what)
+ .flush();
+ super.sendMessage(msg);
+ }
+
+ /**
+ * Reply to srcMsg
+ *
+ * @param srcMsg
+ * @param dstMsg
+ */
+ @Override
+ public void replyToMessage(Message srcMsg, Message dstMsg) {
+ getOrInitLog()
+ .trace("replyToMessage recvdMessage=% sendingUid=% sentMessage=%")
+ .c(srcMsg.what)
+ .c(srcMsg.sendingUid)
+ .c(dstMsg.what)
+ .flush();
+ super.replyToMessage(srcMsg, dstMsg);
+ }
+
+ /**
+ * Send the Message synchronously.
+ *
+ * @param msg to send
+ * @return reply message or null if an error.
+ */
+ @Override
+ public Message sendMessageSynchronously(Message msg) {
+ getOrInitLog().trace("sendMessageSynchronously.send message=%")
+ .c(msg.what)
+ .flush();
+ Message replyMessage = super.sendMessageSynchronously(msg);
+ getOrInitLog().trace("sendMessageSynchronously.recv message=% sendingUid=%")
+ .c(replyMessage.what)
+ .c(replyMessage.sendingUid)
+ .flush();
+ return replyMessage;
+ }
+
+ @VisibleForTesting
+ public void setWifiLog(WifiLog log) {
+ mLog = log;
+ }
+}
diff --git a/service/java/com/android/server/wifi/util/WifiHandler.java b/service/java/com/android/server/wifi/util/WifiHandler.java
new file mode 100644
index 0000000..8750601
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/WifiHandler.java
@@ -0,0 +1,68 @@
+/*
+ * 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.annotation.NonNull;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.WifiLog;
+
+/**
+ * This class subclasses Handler to log incoming messages
+ */
+public class WifiHandler extends Handler {
+ private static final String LOG_TAG = "WifiHandler";
+ private WifiLog mLog;
+ private String mTag;
+
+ public WifiHandler(String tag, Looper looper) {
+ super(looper);
+ mTag = LOG_TAG + "." + tag;
+ }
+
+ @NonNull
+ private WifiLog getOrInitLog() {
+ // Lazy initialization of mLog
+ if (mLog == null) {
+ mLog = WifiInjector.getInstance().makeLog(mTag);
+ }
+ return mLog;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ getOrInitLog().trace("Received message=%d sendingUid=%")
+ .c(msg.what)
+ .c(msg.sendingUid)
+ .flush();
+ }
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public void setWifiLog(WifiLog wifiLog) {
+ // TODO WifiInjector should be passed as a variable in the constructor
+ // b/33308811 tracks removing lazy initializations of mLog
+ mLog = wifiLog;
+ }
+}
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
new file mode 100644
index 0000000..f945437
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java
@@ -0,0 +1,242 @@
+/*
+ * 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.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.net.ConnectivityManager;
+import android.net.NetworkScoreManager;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.WifiLog;
+import com.android.server.wifi.WifiSettingsStore;
+
+import java.util.List;
+
+/**
+ * A wifi permissions utility assessing permissions
+ * for getting scan results by a package.
+ */
+public class WifiPermissionsUtil {
+ private static final String TAG = "WifiPermissionsUtil";
+ private final WifiPermissionsWrapper mWifiPermissionsWrapper;
+ private final Context mContext;
+ private final AppOpsManager mAppOps;
+ private final UserManager mUserManager;
+ private final WifiSettingsStore mSettingsStore;
+ private final NetworkScoreManager mNetworkScoreManager;
+ private WifiLog mLog;
+
+ public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper,
+ Context context, WifiSettingsStore settingsStore, UserManager userManager,
+ NetworkScoreManager networkScoreManager, WifiInjector wifiInjector) {
+ mWifiPermissionsWrapper = wifiPermissionsWrapper;
+ mContext = context;
+ mUserManager = userManager;
+ mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mSettingsStore = settingsStore;
+ mLog = wifiInjector.makeLog(TAG);
+ mNetworkScoreManager = networkScoreManager;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean checkConfigOverridePermission(int uid) {
+ try {
+ int permission = mWifiPermissionsWrapper.getOverrideWifiConfigPermission(uid);
+ return (permission == PackageManager.PERMISSION_GRANTED);
+ } catch (RemoteException e) {
+ mLog.err("Error checking for permission: %").r(e.getMessage()).flush();
+ return false;
+ }
+ }
+
+ /**
+ * Check and enforce tether change permission.
+ *
+ * @param context Context object of the caller.
+ */
+ public void enforceTetherChangePermission(Context context) {
+ ConnectivityManager.enforceTetherChangePermission(context);
+ }
+
+ /**
+ * Check and enforce Location permission.
+ *
+ * @param pkgName PackageName of the application requesting access
+ * @param uid The uid of the package
+ */
+ public void enforceLocationPermission(String pkgName, int uid) {
+ if (!checkCallersLocationPermission(pkgName, uid)) {
+ throw new SecurityException("UID " + uid + " does not have Location permission");
+ }
+ }
+
+ /**
+ * API to determine if the caller has permissions to get
+ * scan results.
+ * @param pkgName Packagename of the application requesting access
+ * @param uid The uid of the package
+ * @param minVersion Minimum app API Version number to enforce location permission
+ * @return boolean true or false if permissions is granted
+ */
+ public boolean canAccessScanResults(String pkgName, int uid,
+ int minVersion) throws SecurityException {
+ mAppOps.checkPackage(uid, pkgName);
+ // Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS
+ // permission or is an Active Nw scorer.
+ boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid)
+ || isCallerActiveNwScorer(uid);
+ // LocationAccess by App: For AppVersion older than minVersion,
+ // it is sufficient to check if the App is foreground.
+ // Otherwise, Location Mode must be enabled and caller must have
+ // Coarse Location permission to have access to location information.
+ boolean canAppPackageUseLocation = isLegacyForeground(pkgName, minVersion)
+ || (isLocationModeEnabled(pkgName)
+ && checkCallersLocationPermission(pkgName, uid));
+ // If neither caller or app has location access, there is no need to check
+ // any other permissions. Deny access to scan results.
+ if (!canCallingUidAccessLocation && !canAppPackageUseLocation) {
+ mLog.tC("Denied: no location permission");
+ return false;
+ }
+ // Check if Wifi Scan request is an operation allowed for this App.
+ if (!isScanAllowedbyApps(pkgName, uid)) {
+ mLog.tC("Denied: app wifi scan not allowed");
+ return false;
+ }
+ // If the User or profile is current, permission is granted
+ // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+ if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) {
+ mLog.tC("Denied: Profile not permitted");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if the caller holds PEERS_MAC_ADDRESS permission.
+ */
+ private boolean checkCallerHasPeersMacAddressPermission(int uid) {
+ return mWifiPermissionsWrapper.getUidPermission(
+ android.Manifest.permission.PEERS_MAC_ADDRESS, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Returns true if the caller is an Active Network Scorer.
+ */
+ private boolean isCallerActiveNwScorer(int uid) {
+ return mNetworkScoreManager.isCallerActiveScorer(uid);
+ }
+
+ /**
+ * Returns true if Wifi scan operation is allowed for this caller
+ * and package.
+ */
+ private boolean isScanAllowedbyApps(String pkgName, int uid) {
+ return checkAppOpAllowed(AppOpsManager.OP_WIFI_SCAN, pkgName, uid);
+ }
+
+ /**
+ * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
+ */
+ private boolean checkInteractAcrossUsersFull(int uid) {
+ return mWifiPermissionsWrapper.getUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Returns true if the calling user is the current one or a profile of the
+ * current user.
+ */
+ private boolean isCurrentProfile(int uid) {
+ int currentUser = mWifiPermissionsWrapper.getCurrentUser();
+ int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid);
+ if (callingUserId == currentUser) {
+ return true;
+ } else {
+ List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser);
+ for (UserInfo user: userProfiles) {
+ if (user.id == callingUserId) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the App version is older than minVersion.
+ */
+ private boolean isLegacyVersion(String pkgName, int minVersion) {
+ try {
+ if (mContext.getPackageManager().getApplicationInfo(pkgName, 0)
+ .targetSdkVersion < minVersion) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // In case of exception, assume known app (more strict checking)
+ // Note: This case will never happen since checkPackage is
+ // called to verify valididity before checking App's version.
+ }
+ return false;
+ }
+
+ private boolean checkAppOpAllowed(int op, String pkgName, int uid) {
+ return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED;
+ }
+
+ private boolean isLegacyForeground(String pkgName, int version) {
+ return isLegacyVersion(pkgName, version) && isForegroundApp(pkgName);
+ }
+
+ private boolean isForegroundApp(String pkgName) {
+ return pkgName.equals(mWifiPermissionsWrapper.getTopPkgName());
+ }
+
+ /**
+ * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION
+ * and a corresponding app op is allowed for this package and uid.
+ */
+ private boolean checkCallersLocationPermission(String pkgName, int uid) {
+ // Coarse Permission implies Fine permission
+ if ((mWifiPermissionsWrapper.getUidPermission(
+ Manifest.permission.ACCESS_COARSE_LOCATION, uid)
+ == PackageManager.PERMISSION_GRANTED)
+ && checkAppOpAllowed(AppOpsManager.OP_COARSE_LOCATION, pkgName, uid)) {
+ return true;
+ }
+ return false;
+ }
+ private boolean isLocationModeEnabled(String pkgName) {
+ // Location mode check on applications that are later than version.
+ return (mSettingsStore.getLocationModeSetting(mContext)
+ != Settings.Secure.LOCATION_MODE_OFF);
+ }
+}
diff --git a/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
new file mode 100644
index 0000000..6ca2f02
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/WifiPermissionsWrapper.java
@@ -0,0 +1,98 @@
+/*
+ * 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.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.server.LocalServices;
+
+import java.util.List;
+
+/**
+ * A wifi permissions dependency class to wrap around external
+ * calls to static methods that enable testing.
+ */
+public class WifiPermissionsWrapper {
+ private static final String TAG = "WifiPermissionsWrapper";
+ private final Context mContext;
+
+ public WifiPermissionsWrapper(Context context) {
+ mContext = context;
+ }
+
+ public int getCurrentUser() {
+ return ActivityManager.getCurrentUser();
+ }
+
+ /**
+ * Returns the user ID corresponding to the UID
+ * @param uid Calling Uid
+ * @return userid Corresponding user id
+ */
+ public int getCallingUserId(int uid) {
+ return UserHandle.getUserId(uid);
+ }
+
+ /**
+ * Get the PackageName of the top running task
+ * @return String corresponding to the package
+ */
+ public String getTopPkgName() {
+ ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ String topTaskPkg = " ";
+ List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
+ if (!tasks.isEmpty()) {
+ return tasks.get(0).topActivity.getPackageName();
+ }
+ return topTaskPkg;
+ }
+
+ /**
+ * API is wrap around ActivityManager class to
+ * get location permissions for a certain UID
+ * @param: Manifest permission string
+ * @param: Uid to get permission for
+ * @return: Permissions setting
+ */
+ public int getUidPermission(String permissionType, int uid) {
+ return ActivityManager.checkUidPermission(permissionType, uid);
+ }
+
+ /**
+ * Gets the local service {link@ DevicePolicyManagerInternal}, can be null
+ */
+ public DevicePolicyManagerInternal getDevicePolicyManagerInternal() {
+ return LocalServices.getService(DevicePolicyManagerInternal.class);
+ }
+
+ /**
+ * Determines if the caller has the override wifi config permission.
+ *
+ * @param uid to check the permission for
+ * @return int representation of success or denied
+ * @throws RemoteException
+ */
+ public int getOverrideWifiConfigPermission(int uid) throws RemoteException {
+ return AppGlobals.getPackageManager().checkUidPermission(
+ android.Manifest.permission.OVERRIDE_WIFI_CONFIG, uid);
+ }
+}
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..853136b
--- /dev/null
+++ b/service/java/com/android/server/wifi/util/XmlUtil.java
@@ -0,0 +1,1105 @@
+/*
+ * 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.Arrays;
+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>
+ *
+ * Note: These utility methods are meant to be used for:
+ * 1. Backup/restore wifi network data to/from cloud.
+ * 2. Persisting wifi network data to/from disk.
+ */
+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_REQUIRE_PMF = "RequirePMF";
+ 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_ALLOWED_GROUP_CIPHERS = "AllowedGroupCiphers";
+ public static final String XML_TAG_ALLOWED_PAIRWISE_CIPHERS = "AllowedPairwiseCiphers";
+ public static final String XML_TAG_SHARED = "Shared";
+ public static final String XML_TAG_STATUS = "Status";
+ 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";
+ public static final String XML_TAG_IS_LEGACY_PASSPOINT_CONFIG = "IsLegacyPasspointConfig";
+ public static final String XML_TAG_ROAMING_CONSORTIUM_OIS = "RoamingConsortiumOIs";
+
+ /**
+ * 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_REQUIRE_PMF, configuration.requirePMF);
+ 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_ALLOWED_GROUP_CIPHERS,
+ configuration.allowedGroupCiphers.toByteArray());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_ALLOWED_PAIRWISE_CIPHERS,
+ configuration.allowedPairwiseCiphers.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_STATUS, configuration.status);
+ 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);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_IS_LEGACY_PASSPOINT_CONFIG,
+ configuration.isLegacyPasspointConfig);
+ XmlUtil.writeNextValue(
+ out, XML_TAG_ROAMING_CONSORTIUM_OIS, configuration.roamingConsortiumIds);
+ }
+
+ /**
+ * 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_REQUIRE_PMF:
+ configuration.requirePMF = (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_ALLOWED_GROUP_CIPHERS:
+ byte[] allowedGroupCiphers = (byte[]) value;
+ configuration.allowedGroupCiphers = BitSet.valueOf(allowedGroupCiphers);
+ break;
+ case XML_TAG_ALLOWED_PAIRWISE_CIPHERS:
+ byte[] allowedPairwiseCiphers = (byte[]) value;
+ configuration.allowedPairwiseCiphers =
+ BitSet.valueOf(allowedPairwiseCiphers);
+ break;
+ case XML_TAG_SHARED:
+ configuration.shared = (boolean) value;
+ break;
+ case XML_TAG_STATUS:
+ int status = (int) value;
+ // Any network which was CURRENT before reboot needs
+ // to be restored to ENABLED.
+ if (status == WifiConfiguration.Status.CURRENT) {
+ status = WifiConfiguration.Status.ENABLED;
+ }
+ configuration.status = status;
+ 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;
+ case XML_TAG_IS_LEGACY_PASSPOINT_CONFIG:
+ configuration.isLegacyPasspointConfig = (boolean) value;
+ break;
+ case XML_TAG_ROAMING_CONSORTIUM_OIS:
+ configuration.roamingConsortiumIds = (long[]) 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 selectionStatus NetworkSelectionStatus object to be serialized.
+ */
+ public static void writeToXml(XmlSerializer out, NetworkSelectionStatus selectionStatus)
+ throws XmlPullParserException, IOException {
+ XmlUtil.writeNextValue(
+ out, XML_TAG_SELECTION_STATUS, selectionStatus.getNetworkStatusString());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_DISABLE_REASON, selectionStatus.getNetworkDisableReasonString());
+ XmlUtil.writeNextValue(out, XML_TAG_CONNECT_CHOICE, selectionStatus.getConnectChoice());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_CONNECT_CHOICE_TIMESTAMP,
+ selectionStatus.getConnectChoiceTimestamp());
+ XmlUtil.writeNextValue(
+ out, XML_TAG_HAS_EVER_CONNECTED, selectionStatus.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 selectionStatus = new NetworkSelectionStatus();
+ String statusString = "";
+ String disableReasonString = "";
+
+ // 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:
+ statusString = (String) value;
+ break;
+ case XML_TAG_DISABLE_REASON:
+ disableReasonString = (String) value;
+ break;
+ case XML_TAG_CONNECT_CHOICE:
+ selectionStatus.setConnectChoice((String) value);
+ break;
+ case XML_TAG_CONNECT_CHOICE_TIMESTAMP:
+ selectionStatus.setConnectChoiceTimestamp((long) value);
+ break;
+ case XML_TAG_HAS_EVER_CONNECTED:
+ selectionStatus.setHasEverConnected((boolean) value);
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown value name found: " + valueName[0]);
+ }
+ }
+ // Now figure out the network selection status codes from |selectionStatusString| &
+ // |disableReasonString|.
+ int status =
+ Arrays.asList(NetworkSelectionStatus.QUALITY_NETWORK_SELECTION_STATUS)
+ .indexOf(statusString);
+ int disableReason =
+ Arrays.asList(NetworkSelectionStatus.QUALITY_NETWORK_SELECTION_DISABLE_REASON)
+ .indexOf(disableReasonString);
+
+ // If either of the above codes are invalid or if the network was temporarily disabled
+ // (blacklisted), restore the status as enabled. We don't want to persist blacklists
+ // across reboots.
+ if (status == -1 || disableReason == -1 ||
+ status == NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED) {
+ status = NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
+ disableReason = NetworkSelectionStatus.NETWORK_SELECTION_ENABLE;
+ }
+ selectionStatus.setNetworkSelectionStatus(status);
+ selectionStatus.setNetworkSelectionDisableReason(disableReason);
+ return selectionStatus;
+ }
+ }
+
+ /**
+ * 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";
+ public static final String XML_TAG_PLMN = "PLMN";
+ public static final String XML_TAG_REALM = "Realm";
+
+ /**
+ * 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());
+ XmlUtil.writeNextValue(out, XML_TAG_PLMN, enterpriseConfig.getPlmn());
+ XmlUtil.writeNextValue(out, XML_TAG_REALM, enterpriseConfig.getRealm());
+ }
+
+ /**
+ * 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;
+ case XML_TAG_PLMN:
+ enterpriseConfig.setPlmn((String) value);
+ break;
+ case XML_TAG_REALM:
+ enterpriseConfig.setRealm((String) value);
+ break;
+ default:
+ throw new XmlPullParserException(
+ "Unknown value name found: " + valueName[0]);
+ }
+ }
+ return enterpriseConfig;
+ }
+ }
+}
+
diff --git a/service/java/com/android/server/wifi/wificond/ChannelSettings.java b/service/java/com/android/server/wifi/wificond/ChannelSettings.java
new file mode 100644
index 0000000..7b20983
--- /dev/null
+++ b/service/java/com/android/server/wifi/wificond/ChannelSettings.java
@@ -0,0 +1,95 @@
+/*
+ * 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.wificond;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * ChannelSettings for wificond
+ *
+ * @hide
+ */
+public class ChannelSettings implements Parcelable {
+ private static final String TAG = "ChannelSettings";
+
+ public int frequency;
+
+ /** public constructor */
+ public ChannelSettings() { }
+
+ /** override comparator */
+ @Override
+ public boolean equals(Object rhs) {
+ if (this == rhs) return true;
+ if (!(rhs instanceof ChannelSettings)) {
+ return false;
+ }
+ ChannelSettings channel = (ChannelSettings) rhs;
+ if (channel == null) {
+ return false;
+ }
+ return frequency == channel.frequency;
+ }
+
+ /** override hash code */
+ @Override
+ public int hashCode() {
+ return Objects.hash(frequency);
+ }
+
+ /** implement Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * implement Parcelable interface
+ * |flags| is ignored.
+ * */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(frequency);
+ }
+
+ /** implement Parcelable interface */
+ public static final Parcelable.Creator<ChannelSettings> CREATOR =
+ new Parcelable.Creator<ChannelSettings>() {
+ /**
+ * Caller is responsible for providing a valid parcel.
+ */
+ @Override
+ public ChannelSettings createFromParcel(Parcel in) {
+ ChannelSettings result = new ChannelSettings();
+ result.frequency = in.readInt();
+ if (in.dataAvail() != 0) {
+ Log.e(TAG, "Found trailing data after parcel parsing.");
+ }
+
+ return result;
+ }
+
+ @Override
+ public ChannelSettings[] newArray(int size) {
+ return new ChannelSettings[size];
+ }
+ };
+}
diff --git a/service/java/com/android/server/wifi/wificond/HiddenNetwork.java b/service/java/com/android/server/wifi/wificond/HiddenNetwork.java
new file mode 100644
index 0000000..def3b43
--- /dev/null
+++ b/service/java/com/android/server/wifi/wificond/HiddenNetwork.java
@@ -0,0 +1,92 @@
+/*
+ * 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.wificond;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * HiddenNetwork for wificond
+ *
+ * @hide
+ */
+public class HiddenNetwork implements Parcelable {
+ private static final String TAG = "HiddenNetwork";
+
+ public byte[] ssid;
+
+ /** public constructor */
+ public HiddenNetwork() { }
+
+ /** override comparator */
+ @Override
+ public boolean equals(Object rhs) {
+ if (this == rhs) return true;
+ if (!(rhs instanceof HiddenNetwork)) {
+ return false;
+ }
+ HiddenNetwork network = (HiddenNetwork) rhs;
+ return java.util.Arrays.equals(ssid, network.ssid);
+ }
+
+ /** override hash code */
+ @Override
+ public int hashCode() {
+ return Objects.hash(ssid);
+ }
+
+ /** implement Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * implement Parcelable interface
+ * |flags| is ignored.
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeByteArray(ssid);
+ }
+
+ /** implement Parcelable interface */
+ public static final Parcelable.Creator<HiddenNetwork> CREATOR =
+ new Parcelable.Creator<HiddenNetwork>() {
+ /**
+ * Caller is responsible for providing a valid parcel.
+ */
+ @Override
+ public HiddenNetwork createFromParcel(Parcel in) {
+ HiddenNetwork result = new HiddenNetwork();
+ result.ssid = in.createByteArray();
+ if (in.dataAvail() != 0) {
+ Log.e(TAG, "Found trailing data after parcel parsing.");
+ }
+
+ return result;
+ }
+
+ @Override
+ public HiddenNetwork[] newArray(int size) {
+ return new HiddenNetwork[size];
+ }
+ };
+}
diff --git a/service/java/com/android/server/wifi/wificond/NativeScanResult.java b/service/java/com/android/server/wifi/wificond/NativeScanResult.java
new file mode 100644
index 0000000..d41237a
--- /dev/null
+++ b/service/java/com/android/server/wifi/wificond/NativeScanResult.java
@@ -0,0 +1,109 @@
+/*
+ * 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.wificond;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.BitSet;
+
+/**
+ * ScanResult from wificond
+ *
+ * @hide
+ */
+public class NativeScanResult implements Parcelable {
+ private static final int CAPABILITY_SIZE = 16;
+
+ public byte[] ssid;
+ public byte[] bssid;
+ public byte[] infoElement;
+ public int frequency;
+ public int signalMbm;
+ public long tsf;
+ public BitSet capability;
+ public boolean associated;
+
+ /** public constructor */
+ public NativeScanResult() { }
+
+ /** copy constructor */
+ public NativeScanResult(NativeScanResult source) {
+ ssid = source.ssid.clone();
+ bssid = source.bssid.clone();
+ infoElement = source.infoElement.clone();
+ frequency = source.frequency;
+ signalMbm = source.signalMbm;
+ tsf = source.tsf;
+ capability = (BitSet) source.capability.clone();
+ associated = source.associated;
+ }
+
+ /** implement Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** implement Parcelable interface */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeByteArray(ssid);
+ out.writeByteArray(bssid);
+ out.writeByteArray(infoElement);
+ out.writeInt(frequency);
+ out.writeInt(signalMbm);
+ out.writeLong(tsf);
+ int capabilityInt = 0;
+ for (int i = 0; i < CAPABILITY_SIZE; i++) {
+ if (capability.get(i)) {
+ capabilityInt |= 1 << i;
+ }
+ }
+ out.writeInt(capabilityInt);
+ out.writeInt(associated ? 1 : 0);
+ }
+
+ /** implement Parcelable interface */
+ public static final Parcelable.Creator<NativeScanResult> CREATOR =
+ new Parcelable.Creator<NativeScanResult>() {
+ @Override
+ public NativeScanResult createFromParcel(Parcel in) {
+ NativeScanResult result = new NativeScanResult();
+ result.ssid = in.createByteArray();
+ result.bssid = in.createByteArray();
+ result.infoElement = in.createByteArray();
+ result.frequency = in.readInt();
+ result.signalMbm = in.readInt();
+ result.tsf = in.readLong();
+ int capabilityInt = in.readInt();
+ result.capability = new BitSet(CAPABILITY_SIZE);
+ for (int i = 0; i < CAPABILITY_SIZE; i++) {
+ if ((capabilityInt & (1 << i)) != 0) {
+ result.capability.set(i);
+ }
+ }
+ result.associated = (in.readInt() != 0);
+ return result;
+ }
+
+ @Override
+ public NativeScanResult[] newArray(int size) {
+ return new NativeScanResult[size];
+ }
+ };
+}
diff --git a/service/java/com/android/server/wifi/wificond/OWNERS b/service/java/com/android/server/wifi/wificond/OWNERS
new file mode 100644
index 0000000..dab2f95
--- /dev/null
+++ b/service/java/com/android/server/wifi/wificond/OWNERS
@@ -0,0 +1 @@
+nywang@google.com
diff --git a/service/java/com/android/server/wifi/wificond/PnoNetwork.java b/service/java/com/android/server/wifi/wificond/PnoNetwork.java
new file mode 100644
index 0000000..e05e993
--- /dev/null
+++ b/service/java/com/android/server/wifi/wificond/PnoNetwork.java
@@ -0,0 +1,87 @@
+/*
+ * 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.wificond;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * PnoNetwork for wificond
+ *
+ * @hide
+ */
+public class PnoNetwork implements Parcelable {
+
+ public boolean isHidden;
+ public byte[] ssid;
+
+ /** public constructor */
+ public PnoNetwork() { }
+
+ /** override comparator */
+ @Override
+ public boolean equals(Object rhs) {
+ if (this == rhs) return true;
+ if (!(rhs instanceof PnoNetwork)) {
+ return false;
+ }
+ PnoNetwork network = (PnoNetwork) rhs;
+ return java.util.Arrays.equals(ssid, network.ssid)
+ && isHidden == network.isHidden;
+ }
+
+ /** override hash code */
+ @Override
+ public int hashCode() {
+ return Objects.hash(isHidden, ssid);
+ }
+
+ /** implement Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * implement Parcelable interface
+ * |flag| is ignored.
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(isHidden ? 1 : 0);
+ out.writeByteArray(ssid);
+ }
+
+ /** implement Parcelable interface */
+ public static final Parcelable.Creator<PnoNetwork> CREATOR =
+ new Parcelable.Creator<PnoNetwork>() {
+ @Override
+ public PnoNetwork createFromParcel(Parcel in) {
+ PnoNetwork result = new PnoNetwork();
+ result.isHidden = in.readInt() != 0 ? true : false;
+ result.ssid = in.createByteArray();
+ return result;
+ }
+
+ @Override
+ public PnoNetwork[] newArray(int size) {
+ return new PnoNetwork[size];
+ }
+ };
+}
diff --git a/service/java/com/android/server/wifi/wificond/PnoSettings.java b/service/java/com/android/server/wifi/wificond/PnoSettings.java
new file mode 100644
index 0000000..51a7cc1
--- /dev/null
+++ b/service/java/com/android/server/wifi/wificond/PnoSettings.java
@@ -0,0 +1,101 @@
+/*
+ * 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.wificond;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+/**
+ * PnoSettings for wificond
+ *
+ * @hide
+ */
+public class PnoSettings implements Parcelable {
+ public int intervalMs;
+ public int min2gRssi;
+ public int min5gRssi;
+ public ArrayList<PnoNetwork> pnoNetworks;
+
+ /** public constructor */
+ public PnoSettings() { }
+
+ /** override comparator */
+ @Override
+ public boolean equals(Object rhs) {
+ if (this == rhs) return true;
+ if (!(rhs instanceof PnoSettings)) {
+ return false;
+ }
+ PnoSettings settings = (PnoSettings) rhs;
+ if (settings == null) {
+ return false;
+ }
+ return intervalMs == settings.intervalMs
+ && min2gRssi == settings.min2gRssi
+ && min5gRssi == settings.min5gRssi
+ && pnoNetworks.equals(settings.pnoNetworks);
+ }
+
+ /** override hash code */
+ @Override
+ public int hashCode() {
+ return Objects.hash(intervalMs, min2gRssi, min5gRssi, pnoNetworks);
+ }
+
+ /** implement Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * implement Parcelable interface
+ * |flag| is ignored.
+ * */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(intervalMs);
+ out.writeInt(min2gRssi);
+ out.writeInt(min5gRssi);
+ out.writeTypedList(pnoNetworks);
+ }
+
+ /** implement Parcelable interface */
+ public static final Parcelable.Creator<PnoSettings> CREATOR =
+ new Parcelable.Creator<PnoSettings>() {
+ @Override
+ public PnoSettings createFromParcel(Parcel in) {
+ PnoSettings result = new PnoSettings();
+ result.intervalMs = in.readInt();
+ result.min2gRssi = in.readInt();
+ result.min5gRssi = in.readInt();
+
+ result.pnoNetworks = new ArrayList<PnoNetwork>();
+ in.readTypedList(result.pnoNetworks, PnoNetwork.CREATOR);
+
+ return result;
+ }
+
+ @Override
+ public PnoSettings[] newArray(int size) {
+ return new PnoSettings[size];
+ }
+ };
+}
diff --git a/service/java/com/android/server/wifi/wificond/SingleScanSettings.java b/service/java/com/android/server/wifi/wificond/SingleScanSettings.java
new file mode 100644
index 0000000..02950f0
--- /dev/null
+++ b/service/java/com/android/server/wifi/wificond/SingleScanSettings.java
@@ -0,0 +1,103 @@
+/*
+ * 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.wificond;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+/**
+ * SingleScanSettings for wificond
+ *
+ * @hide
+ */
+public class SingleScanSettings implements Parcelable {
+ private static final String TAG = "SingleScanSettings";
+
+ public ArrayList<ChannelSettings> channelSettings;
+ public ArrayList<HiddenNetwork> hiddenNetworks;
+
+ /** public constructor */
+ public SingleScanSettings() { }
+
+ /** override comparator */
+ @Override
+ public boolean equals(Object rhs) {
+ if (this == rhs) return true;
+ if (!(rhs instanceof SingleScanSettings)) {
+ return false;
+ }
+ SingleScanSettings settings = (SingleScanSettings) rhs;
+ if (settings == null) {
+ return false;
+ }
+ return channelSettings.equals(settings.channelSettings)
+ && hiddenNetworks.equals(settings.hiddenNetworks);
+ }
+
+ /** override hash code */
+ @Override
+ public int hashCode() {
+ return Objects.hash(channelSettings, hiddenNetworks);
+ }
+
+
+ /** implement Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * implement Parcelable interface
+ * |flags| is ignored.
+ */
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeTypedList(channelSettings);
+ out.writeTypedList(hiddenNetworks);
+ }
+
+ /** implement Parcelable interface */
+ public static final Parcelable.Creator<SingleScanSettings> CREATOR =
+ new Parcelable.Creator<SingleScanSettings>() {
+ /**
+ * Caller is responsible for providing a valid parcel.
+ */
+ @Override
+ public SingleScanSettings createFromParcel(Parcel in) {
+ SingleScanSettings result = new SingleScanSettings();
+ result.channelSettings = new ArrayList<ChannelSettings>();
+ in.readTypedList(result.channelSettings, ChannelSettings.CREATOR);
+ result.hiddenNetworks = new ArrayList<HiddenNetwork>();
+ in.readTypedList(result.hiddenNetworks, HiddenNetwork.CREATOR);
+ if (in.dataAvail() != 0) {
+ Log.e(TAG, "Found trailing data after parcel parsing.");
+ }
+
+ return result;
+ }
+
+ @Override
+ public SingleScanSettings[] newArray(int size) {
+ return new SingleScanSettings[size];
+ }
+ };
+}
diff --git a/service/jni/com_android_server_wifi_WifiNative.cpp b/service/jni/com_android_server_wifi_WifiNative.cpp
index ffd5b55..cf35028 100644
--- a/service/jni/com_android_server_wifi_WifiNative.cpp
+++ b/service/jni/com_android_server_wifi_WifiNative.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2008, The Android Open Source Project
+ * Copyright 2017, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,2460 +14,22 @@
* limitations under the License.
*/
-#define LOG_TAG "wifi"
+#define LOG_TAG "wifi-jni"
-#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 <linux/if_arp.h>
-#include <algorithm>
-#include <limits>
-#include <vector>
+#include <log/log.h>
+#include <nativehelper/JniConstants.h>
+#include <nativehelper/ScopedBytes.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/jni.h>
-#include "wifi.h"
-#include "wifi_hal.h"
#include "jni_helper.h"
-#include "rtt.h"
-#include "wifi_hal_stub.h"
-#define REPLY_BUF_SIZE 4096 + 1 // wpa_supplicant's maximum size + 1 for nul
-#define EVENT_BUF_SIZE 2048
-#define WAKE_REASON_TYPE_MAX 10
namespace android {
-extern "C"
-jint Java_com_android_server_wifi_WifiNative_registerNanNatives(JNIEnv* env, jclass clazz);
-
-static jint DBG = false;
-constexpr int SAFE_NET_LOG_ID = 0x534e4554;
-
-//Please put all HAL function call here and call from the function table instead of directly call
-wifi_hal_fn hal_fn;
-static bool doCommand(JNIEnv* env, jstring javaCommand,
- char* reply, size_t reply_len) {
- ScopedUtfChars command(env, javaCommand);
- if (command.c_str() == NULL) {
- return false; // ScopedUtfChars already threw on error.
- }
-
- if (DBG) {
- ALOGD("doCommand: %s", command.c_str());
- }
-
- --reply_len; // Ensure we have room to add NUL termination.
- if (::wifi_command(command.c_str(), reply, &reply_len) != 0) {
- return false;
- }
-
- // Strip off trailing newline.
- if (reply_len > 0 && reply[reply_len-1] == '\n') {
- reply[reply_len-1] = '\0';
- } else {
- reply[reply_len] = '\0';
- }
- return true;
-}
-
-static jint doIntCommand(JNIEnv* env, jstring javaCommand) {
- char reply[REPLY_BUF_SIZE];
- if (!doCommand(env, javaCommand, reply, sizeof(reply))) {
- return -1;
- }
- return static_cast<jint>(atoi(reply));
-}
-
-static jboolean doBooleanCommand(JNIEnv* env, jstring javaCommand) {
- char reply[REPLY_BUF_SIZE];
- if (!doCommand(env, javaCommand, reply, sizeof(reply))) {
- return JNI_FALSE;
- }
- jboolean result = (strcmp(reply, "OK") == 0);
- if (!result) {
- ScopedUtfChars command(env, javaCommand);
- ALOGI("command '%s' returned '%s", command.c_str(), reply);
- }
- return result;
-}
-
-// Send a command to the supplicant, and return the reply as a String.
-static jstring doStringCommand(JNIEnv* env, jstring javaCommand) {
- char reply[REPLY_BUF_SIZE];
- if (!doCommand(env, javaCommand, reply, sizeof(reply))) {
- return NULL;
- }
- return env->NewStringUTF(reply);
-}
-
-static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jclass)
-{
- return (::is_wifi_driver_loaded() == 1);
-}
-
-static jboolean android_net_wifi_loadDriver(JNIEnv* env, jclass)
-{
- return (::wifi_load_driver() == 0);
-}
-
-static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jclass)
-{
- return (::wifi_unload_driver() == 0);
-}
-
-static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jclass, jboolean p2pSupported)
-{
- return (::wifi_start_supplicant(p2pSupported) == 0);
-}
-
-static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jclass, jboolean p2pSupported)
-{
- return (::wifi_stop_supplicant(p2pSupported) == 0);
-}
-
-static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jclass)
-{
- return (::wifi_connect_to_supplicant() == 0);
-}
-
-static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jclass)
-{
- ::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);
- if (nread > 0) {
- return env->NewStringUTF(buf);
- } else {
- return NULL;
- }
-}
-
-static jboolean android_net_wifi_doBooleanCommand(JNIEnv* env, jclass, jstring javaCommand) {
- return doBooleanCommand(env, javaCommand);
-}
-
-static jint android_net_wifi_doIntCommand(JNIEnv* env, jclass, jstring javaCommand) {
- return doIntCommand(env, javaCommand);
-}
-
-static jstring android_net_wifi_doStringCommand(JNIEnv* env, jclass, jstring javaCommand) {
- return doStringCommand(env,javaCommand);
-}
-
-/* wifi_hal <==> WifiNative bridge */
-
-static jclass mCls; /* saved WifiNative object */
-static JavaVM *mVM; /* saved JVM pointer */
-
-static const char *WifiHandleVarName = "sWifiHalHandle";
-static const char *WifiIfaceHandleVarName = "sWifiIfaceHandles";
-
-wifi_handle getWifiHandle(JNIHelper &helper, jclass cls) {
- return (wifi_handle) helper.getStaticLongField(cls, WifiHandleVarName);
-}
-
-wifi_interface_handle getIfaceHandle(JNIHelper &helper, jclass cls, jint index) {
- return (wifi_interface_handle) helper.getStaticLongArrayField(cls, WifiIfaceHandleVarName, index);
-}
-
-jboolean setSSIDField(JNIHelper helper, jobject scanResult, const char *rawSsid) {
-
- int len = strlen(rawSsid);
-
- if (len > 0) {
- JNIObject<jbyteArray> ssidBytes = helper.newByteArray(len);
- helper.setByteArrayRegion(ssidBytes, 0, len, (jbyte *) rawSsid);
- jboolean ret = helper.callStaticMethod(mCls,
- "setSsid", "([BLandroid/net/wifi/ScanResult;)Z", ssidBytes.get(), scanResult);
- return ret;
- } else {
- //empty SSID or SSID start with \0
- return true;
- }
-}
-static JNIObject<jobject> createScanResult(JNIHelper &helper, wifi_scan_result *result,
- bool fill_ie) {
- // ALOGD("creating scan result");
- JNIObject<jobject> scanResult = helper.createObject("android/net/wifi/ScanResult");
- if (scanResult == NULL) {
- ALOGE("Error in creating scan result");
- return JNIObject<jobject>(helper, NULL);
- }
-
- ALOGV("setting SSID to %s", result->ssid);
-
- if (!setSSIDField(helper, scanResult, result->ssid)) {
- ALOGE("Error on set SSID");
- return JNIObject<jobject>(helper, NULL);
- }
-
- char bssid[32];
- sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", result->bssid[0], result->bssid[1],
- result->bssid[2], result->bssid[3], result->bssid[4], result->bssid[5]);
-
- helper.setStringField(scanResult, "BSSID", bssid);
-
- helper.setIntField(scanResult, "level", result->rssi);
- helper.setIntField(scanResult, "frequency", result->channel);
- helper.setLongField(scanResult, "timestamp", result->ts);
-
- if (fill_ie) {
- JNIObject<jbyteArray> elements = helper.newByteArray(result->ie_length);
- if (elements == NULL) {
- ALOGE("Error in allocating elements array, length=%d", result->ie_length);
- return JNIObject<jobject>(helper, NULL);
- }
- jbyte * bytes = (jbyte *)&(result->ie_data[0]);
- helper.setByteArrayRegion(elements, 0, result->ie_length, bytes);
- helper.setObjectField(scanResult, "bytes", "[B", elements);
- }
-
- 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);
-}
-
-static jboolean android_net_wifi_startHal(JNIEnv* env, jclass cls) {
- 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);
- }
-}
-
-void android_net_wifi_hal_cleaned_up_handler(wifi_handle handle) {
- ALOGD("In wifi cleaned up handler");
-
- JNIHelper helper(mVM);
- helper.setStaticLongField(mCls, WifiHandleVarName, 0);
-
- helper.deleteGlobalRef(mCls);
- mCls = NULL;
- mVM = NULL;
-}
-
-static void android_net_wifi_stopHal(JNIEnv* env, jclass cls) {
- ALOGD("In wifi stop Hal");
-
- JNIHelper helper(env);
- wifi_handle halHandle = getWifiHandle(helper, cls);
- if (halHandle == NULL)
- return;
-
- ALOGD("halHandle = %p, mVM = %p, mCls = %p", halHandle, mVM, mCls);
- hal_fn.wifi_cleanup(halHandle, android_net_wifi_hal_cleaned_up_handler);
-}
-
-static void android_net_wifi_waitForHalEvents(JNIEnv* env, jclass cls) {
-
- ALOGD("waitForHalEvents called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
-
- JNIHelper helper(env);
- wifi_handle halHandle = getWifiHandle(helper, cls);
- hal_fn.wifi_event_loop(halHandle);
- set_iface_flags("wlan0", false);
-}
-
-static int android_net_wifi_getInterfaces(JNIEnv *env, jclass cls) {
- int n = 0;
-
- JNIHelper helper(env);
-
- wifi_handle halHandle = getWifiHandle(helper, cls);
- wifi_interface_handle *ifaceHandles = NULL;
- int result = hal_fn.wifi_get_ifaces(halHandle, &n, &ifaceHandles);
- if (result < 0) {
- return result;
- }
-
- if (n < 0) {
- THROW(helper,"android_net_wifi_getInterfaces no interfaces");
- return 0;
- }
-
- if (ifaceHandles == NULL) {
- THROW(helper,"android_net_wifi_getInterfaces null interface array");
- return 0;
- }
-
- if (n > 8) {
- THROW(helper,"Too many interfaces");
- return 0;
- }
-
- jlongArray array = (env)->NewLongArray(n);
- if (array == NULL) {
- THROW(helper,"Error in accessing array");
- return 0;
- }
-
- jlong elems[8];
- for (int i = 0; i < n; i++) {
- elems[i] = reinterpret_cast<jlong>(ifaceHandles[i]);
- }
-
- helper.setLongArrayRegion(array, 0, n, elems);
- helper.setStaticLongArrayField(cls, WifiIfaceHandleVarName, array);
-
- return (result < 0) ? result : n;
-}
-
-static jstring android_net_wifi_getInterfaceName(JNIEnv *env, jclass cls, jint i) {
-
- char buf[EVENT_BUF_SIZE];
-
- JNIHelper helper(env);
-
- jlong value = helper.getStaticLongArrayField(cls, WifiIfaceHandleVarName, i);
- wifi_interface_handle handle = (wifi_interface_handle) value;
- int result = hal_fn.wifi_get_iface_name(handle, buf, sizeof(buf));
- if (result < 0) {
- return NULL;
- } else {
- JNIObject<jstring> name = helper.newStringUTF(buf);
- return name.detach();
- }
-}
-
-
-static void onScanEvent(wifi_request_id id, wifi_scan_event event) {
-
- JNIHelper helper(mVM);
-
- // ALOGD("onScanStatus called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
-
- helper.reportEvent(mCls, "onScanStatus", "(II)V", id, event);
-}
-
-static void onFullScanResult(wifi_request_id id, wifi_scan_result *result,
- unsigned buckets_scanned) {
-
- JNIHelper helper(mVM);
-
- //ALOGD("onFullScanResult called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
-
- JNIObject<jobject> scanResult = createScanResult(helper, result, true);
-
- if (scanResult == NULL) {
- return;
- }
-
- helper.reportEvent(mCls, "onFullScanResult", "(ILandroid/net/wifi/ScanResult;II)V", id,
- scanResult.get(), buckets_scanned, (jint) result->capability);
-}
-
-static jboolean android_net_wifi_startScan(
- JNIEnv *env, jclass cls, jint iface, jint id, jobject settings) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- // ALOGD("starting scan on interface[%d] = %p", iface, handle);
-
- wifi_scan_cmd_params params;
- memset(¶ms, 0, sizeof(params));
-
- params.base_period = helper.getIntField(settings, "base_period_ms");
- params.max_ap_per_scan = helper.getIntField(settings, "max_ap_per_scan");
- params.report_threshold_percent = helper.getIntField(settings, "report_threshold_percent");
- params.report_threshold_num_scans = helper.getIntField(settings, "report_threshold_num_scans");
-
- ALOGD("Initialized common fields %d, %d, %d, %d", params.base_period, params.max_ap_per_scan,
- params.report_threshold_percent, params.report_threshold_num_scans);
-
- const char *bucket_array_type = "[Lcom/android/server/wifi/WifiNative$BucketSettings;";
- const char *channel_array_type = "[Lcom/android/server/wifi/WifiNative$ChannelSettings;";
-
- params.num_buckets = helper.getIntField(settings, "num_buckets");
-
- // ALOGD("Initialized num_buckets to %d", params.num_buckets);
-
- for (int i = 0; i < params.num_buckets; i++) {
- JNIObject<jobject> bucket = helper.getObjectArrayField(
- settings, "buckets", bucket_array_type, i);
-
- params.buckets[i].bucket = helper.getIntField(bucket, "bucket");
- params.buckets[i].band = (wifi_band) helper.getIntField(bucket, "band");
- params.buckets[i].period = helper.getIntField(bucket, "period_ms");
- params.buckets[i].max_period = helper.getIntField(bucket, "max_period_ms");
- // Although HAL API allows configurable base value for the truncated
- // exponential back off scan. Native API and above support only
- // truncated binary exponential back off scan.
- // Hard code value of base to 2 here.
- params.buckets[i].base = 2;
- params.buckets[i].step_count = helper.getIntField(bucket, "step_count");
-
- int report_events = helper.getIntField(bucket, "report_events");
- params.buckets[i].report_events = report_events;
-
- if (DBG) {
- ALOGD("bucket[%d] = %d:%d:%d:%d:%d:%d:%d", i, params.buckets[i].bucket,
- params.buckets[i].band, params.buckets[i].period,
- params.buckets[i].max_period, params.buckets[i].base,
- params.buckets[i].step_count, report_events);
- }
-
- params.buckets[i].num_channels = helper.getIntField(bucket, "num_channels");
- // ALOGD("Initialized num_channels to %d", params.buckets[i].num_channels);
-
- for (int j = 0; j < params.buckets[i].num_channels; j++) {
- JNIObject<jobject> channel = helper.getObjectArrayField(
- bucket, "channels", channel_array_type, j);
-
- params.buckets[i].channels[j].channel = helper.getIntField(channel, "frequency");
- params.buckets[i].channels[j].dwellTimeMs = helper.getIntField(channel, "dwell_time_ms");
-
- bool passive = helper.getBoolField(channel, "passive");
- params.buckets[i].channels[j].passive = (passive ? 1 : 0);
-
- // ALOGD("Initialized channel %d", params.buckets[i].channels[j].channel);
- }
- }
-
- // ALOGD("Initialized all fields");
-
- wifi_scan_result_handler handler;
- memset(&handler, 0, sizeof(handler));
- handler.on_full_scan_result = &onFullScanResult;
- handler.on_scan_event = &onScanEvent;
-
- return hal_fn.wifi_start_gscan(id, handle, params, handler) == WIFI_SUCCESS;
-}
-
-static jboolean android_net_wifi_stopScan(JNIEnv *env, jclass cls, jint iface, jint id) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- // ALOGD("stopping scan on interface[%d] = %p", iface, handle);
-
- return hal_fn.wifi_stop_gscan(id, handle) == WIFI_SUCCESS;
-}
-
-static int compare_scan_result_timestamp(const void *v1, const void *v2) {
- const wifi_scan_result *result1 = static_cast<const wifi_scan_result *>(v1);
- const wifi_scan_result *result2 = static_cast<const wifi_scan_result *>(v2);
- return result1->ts - result2->ts;
-}
-
-static jobject android_net_wifi_getScanResults(
- JNIEnv *env, jclass cls, jint iface, jboolean flush) {
-
- JNIHelper helper(env);
- wifi_cached_scan_results scan_data[64];
- int num_scan_data = 64;
-
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- // ALOGD("getting scan results on interface[%d] = %p", iface, handle);
-
- byte b = flush ? 0xFF : 0;
- int result = hal_fn.wifi_get_cached_gscan_results(handle, b, num_scan_data, scan_data, &num_scan_data);
- if (result == WIFI_SUCCESS) {
- JNIObject<jobjectArray> scanData = helper.createObjectArray(
- "android/net/wifi/WifiScanner$ScanData", num_scan_data);
- if (scanData == NULL) {
- ALOGE("Error in allocating array of scanData for getScanResults, length=%d",
- num_scan_data);
- return NULL;
- }
-
- for (int i = 0; i < num_scan_data; i++) {
-
- JNIObject<jobject> data = helper.createObject("android/net/wifi/WifiScanner$ScanData");
- if (data == NULL) {
- ALOGE("Error in allocating scanData for getScanResults");
- return NULL;
- }
-
- helper.setIntField(data, "mId", scan_data[i].scan_id);
- helper.setIntField(data, "mFlags", scan_data[i].flags);
- helper.setIntField(data, "mBucketsScanned", scan_data[i].buckets_scanned);
-
- /* sort all scan results by timestamp */
- qsort(scan_data[i].results, scan_data[i].num_results,
- sizeof(wifi_scan_result), compare_scan_result_timestamp);
-
- JNIObject<jobjectArray> scanResults = helper.createObjectArray(
- "android/net/wifi/ScanResult", scan_data[i].num_results);
- if (scanResults == NULL) {
- ALOGE("Error in allocating scanResult array for getScanResults, length=%d",
- scan_data[i].num_results);
- return NULL;
- }
-
- wifi_scan_result *results = scan_data[i].results;
- for (int j = 0; j < scan_data[i].num_results; j++) {
-
- JNIObject<jobject> scanResult = createScanResult(helper, &results[j], false);
- if (scanResult == NULL) {
- ALOGE("Error in creating scan result for getScanResults");
- return NULL;
- }
-
- helper.setObjectArrayElement(scanResults, j, scanResult);
- }
-
- helper.setObjectField(data, "mResults", "[Landroid/net/wifi/ScanResult;", scanResults);
- helper.setObjectArrayElement(scanData, i, data);
- }
-
- // ALOGD("retrieved %d scan data from interface[%d] = %p", num_scan_data, iface, handle);
- return scanData.detach();
- } else {
- return NULL;
- }
-}
-
-
-static jboolean android_net_wifi_getScanCapabilities(
- JNIEnv *env, jclass cls, jint iface, jobject capabilities) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- // ALOGD("getting scan capabilities on interface[%d] = %p", iface, handle);
-
- wifi_gscan_capabilities c;
- memset(&c, 0, sizeof(c));
- int result = hal_fn.wifi_get_gscan_capabilities(handle, &c);
- if (result != WIFI_SUCCESS) {
- ALOGD("failed to get capabilities : %d", result);
- return JNI_FALSE;
- }
-
- helper.setIntField(capabilities, "max_scan_cache_size", c.max_scan_cache_size);
- helper.setIntField(capabilities, "max_scan_buckets", c.max_scan_buckets);
- helper.setIntField(capabilities, "max_ap_cache_per_scan", c.max_ap_cache_per_scan);
- helper.setIntField(capabilities, "max_rssi_sample_size", c.max_rssi_sample_size);
- helper.setIntField(capabilities, "max_scan_reporting_threshold", c.max_scan_reporting_threshold);
- helper.setIntField(capabilities, "max_hotlist_bssids", c.max_hotlist_bssids);
- helper.setIntField(capabilities, "max_significant_wifi_change_aps",
- c.max_significant_wifi_change_aps);
- helper.setIntField(capabilities, "max_bssid_history_entries", c.max_bssid_history_entries);
- helper.setIntField(capabilities, "max_number_epno_networks", c.max_number_epno_networks);
- helper.setIntField(capabilities, "max_number_epno_networks_by_ssid",
- c.max_number_epno_networks_by_ssid);
- helper.setIntField(capabilities, "max_number_of_white_listed_ssid",
- c.max_number_of_white_listed_ssid);
-
- return JNI_TRUE;
-}
-
-
-static byte parseHexChar(char ch) {
- if (isdigit(ch))
- return ch - '0';
- else if ('A' <= ch && ch <= 'F')
- return ch - 'A' + 10;
- else if ('a' <= ch && ch <= 'f')
- return ch - 'a' + 10;
- else {
- ALOGE("invalid character in bssid %c", ch);
- return 0;
- }
-}
-
-static byte parseHexByte(const char * &str) {
- if (str[0] == '\0') {
- ALOGE("Passed an empty string");
- return 0;
- }
- byte b = parseHexChar(str[0]);
- if (str[1] == '\0' || str[1] == ':') {
- str ++;
- } else {
- b = b << 4 | parseHexChar(str[1]);
- str += 2;
- }
-
- // Skip trailing delimiter if not at the end of the string.
- if (str[0] != '\0') {
- str++;
- }
- return b;
-}
-
-static void parseMacAddress(const char *str, mac_addr addr) {
- addr[0] = parseHexByte(str);
- addr[1] = parseHexByte(str);
- addr[2] = parseHexByte(str);
- addr[3] = parseHexByte(str);
- addr[4] = parseHexByte(str);
- addr[5] = parseHexByte(str);
-}
-
-static bool parseMacAddress(JNIEnv *env, jobject obj, mac_addr addr) {
- JNIHelper helper(env);
- JNIObject<jstring> macAddrString = helper.getStringField(obj, "bssid");
- if (macAddrString == NULL) {
- ALOGE("Error getting bssid field");
- return false;
- }
-
- ScopedUtfChars chars(env, macAddrString);
- const char *bssid = chars.c_str();
- if (bssid == NULL) {
- ALOGE("Error getting bssid");
- return false;
- }
-
- parseMacAddress(bssid, addr);
- return true;
-}
-
-static void onHotlistApFound(wifi_request_id id,
- unsigned num_results, wifi_scan_result *results) {
-
- JNIHelper helper(mVM);
- ALOGD("onHotlistApFound called, vm = %p, obj = %p, num_results = %d", mVM, mCls, num_results);
-
- JNIObject<jobjectArray> scanResults = helper.newObjectArray(num_results,
- "android/net/wifi/ScanResult", NULL);
- if (scanResults == NULL) {
- ALOGE("Error in allocating ScanResult array in onHotlistApFound, length=%d", num_results);
- return;
- }
-
- for (unsigned i = 0; i < num_results; i++) {
-
- JNIObject<jobject> scanResult = createScanResult(helper, &results[i], false);
- if (scanResult == NULL) {
- ALOGE("Error in creating scan result in onHotlistApFound");
- return;
- }
-
- helper.setObjectArrayElement(scanResults, i, scanResult);
-
- ALOGD("Found AP %32s", results[i].ssid);
- }
-
- helper.reportEvent(mCls, "onHotlistApFound", "(I[Landroid/net/wifi/ScanResult;)V",
- id, scanResults.get());
-}
-
-static void onHotlistApLost(wifi_request_id id,
- unsigned num_results, wifi_scan_result *results) {
-
- JNIHelper helper(mVM);
- ALOGD("onHotlistApLost called, vm = %p, obj = %p, num_results = %d", mVM, mCls, num_results);
-
- JNIObject<jobjectArray> scanResults = helper.newObjectArray(num_results,
- "android/net/wifi/ScanResult", NULL);
- if (scanResults == NULL) {
- ALOGE("Error in allocating ScanResult array onHotlistApLost, length=%d", num_results);
- return;
- }
-
- for (unsigned i = 0; i < num_results; i++) {
-
- JNIObject<jobject> scanResult = createScanResult(helper, &results[i], false);
- if (scanResult == NULL) {
- ALOGE("Error in creating scan result in onHotlistApLost");
- return;
- }
-
- helper.setObjectArrayElement(scanResults, i, scanResult);
-
- ALOGD("Lost AP %32s", results[i].ssid);
- }
-
- helper.reportEvent(mCls, "onHotlistApLost", "(I[Landroid/net/wifi/ScanResult;)V",
- id, scanResults.get());
-}
-
-
-static jboolean android_net_wifi_setHotlist(
- JNIEnv *env, jclass cls, jint iface, jint id, jobject ap) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("setting hotlist on interface[%d] = %p", iface, handle);
-
- wifi_bssid_hotlist_params params;
- memset(¶ms, 0, sizeof(params));
-
- params.lost_ap_sample_size = helper.getIntField(ap, "apLostThreshold");
-
- JNIObject<jobjectArray> array = helper.getArrayField(
- ap, "bssidInfos", "[Landroid/net/wifi/WifiScanner$BssidInfo;");
- params.num_bssid = helper.getArrayLength(array);
-
- if (params.num_bssid == 0) {
- ALOGE("setHotlist array length was 0");
- return false;
- }
-
- if (params.num_bssid >
- static_cast<int>(sizeof(params.ap) / sizeof(params.ap[0]))) {
- ALOGE("setHotlist array length is too long");
- android_errorWriteLog(SAFE_NET_LOG_ID, "31856351");
- return false;
- }
-
- for (int i = 0; i < params.num_bssid; i++) {
- JNIObject<jobject> objAp = helper.getObjectArrayElement(array, i);
-
- JNIObject<jstring> macAddrString = helper.getStringField(objAp, "bssid");
- if (macAddrString == NULL) {
- ALOGE("Error getting bssid field");
- return false;
- }
-
- ScopedUtfChars chars(env, macAddrString);
- const char *bssid = chars.c_str();
- if (bssid == NULL) {
- ALOGE("Error getting bssid");
- return false;
- }
- parseMacAddress(bssid, params.ap[i].bssid);
-
- mac_addr addr;
- memcpy(addr, params.ap[i].bssid, sizeof(mac_addr));
-
- char bssidOut[32];
- sprintf(bssidOut, "%0x:%0x:%0x:%0x:%0x:%0x", addr[0], addr[1],
- addr[2], addr[3], addr[4], addr[5]);
-
- ALOGD("Added bssid %s", bssidOut);
-
- params.ap[i].low = helper.getIntField(objAp, "low");
- params.ap[i].high = helper.getIntField(objAp, "high");
- }
-
- wifi_hotlist_ap_found_handler handler;
- memset(&handler, 0, sizeof(handler));
-
- handler.on_hotlist_ap_found = &onHotlistApFound;
- handler.on_hotlist_ap_lost = &onHotlistApLost;
- return hal_fn.wifi_set_bssid_hotlist(id, handle, params, handler) == WIFI_SUCCESS;
-}
-
-static jboolean android_net_wifi_resetHotlist(JNIEnv *env, jclass cls, jint iface, jint id) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("resetting hotlist on interface[%d] = %p", iface, handle);
-
- return hal_fn.wifi_reset_bssid_hotlist(id, handle) == WIFI_SUCCESS;
-}
-
-void onSignificantWifiChange(wifi_request_id id,
- unsigned num_results, wifi_significant_change_result **results) {
-
- JNIHelper helper(mVM);
-
- ALOGD("onSignificantWifiChange called, vm = %p, obj = %p", mVM, mCls);
-
- JNIObject<jobjectArray> scanResults = helper.newObjectArray(
- num_results, "android/net/wifi/ScanResult", NULL);
- if (scanResults == NULL) {
- ALOGE("Error in allocating ScanResult array in onSignificantWifiChange, length=%d",
- num_results);
- return;
- }
-
- for (unsigned i = 0; i < num_results; i++) {
-
- wifi_significant_change_result &result = *(results[i]);
-
- JNIObject<jobject> scanResult = helper.createObject("android/net/wifi/ScanResult");
- if (scanResult == NULL) {
- ALOGE("Error in creating scan result in onSignificantWifiChange");
- return;
- }
-
- // helper.setStringField(scanResult, "SSID", results[i].ssid);
-
- char bssid[32];
- sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", result.bssid[0], result.bssid[1],
- result.bssid[2], result.bssid[3], result.bssid[4], result.bssid[5]);
-
- helper.setStringField(scanResult, "BSSID", bssid);
-
- helper.setIntField(scanResult, "level", result.rssi[0]);
- helper.setIntField(scanResult, "frequency", result.channel);
- // helper.setLongField(scanResult, "timestamp", result.ts);
-
- helper.setObjectArrayElement(scanResults, i, scanResult);
- }
-
- helper.reportEvent(mCls, "onSignificantWifiChange", "(I[Landroid/net/wifi/ScanResult;)V",
- id, scanResults.get());
-
-}
-
-static jboolean android_net_wifi_trackSignificantWifiChange(
- JNIEnv *env, jclass cls, jint iface, jint id, jobject settings) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("tracking significant wifi change on interface[%d] = %p", iface, handle);
-
- wifi_significant_change_params params;
- memset(¶ms, 0, sizeof(params));
-
- params.rssi_sample_size = helper.getIntField(settings, "rssiSampleSize");
- params.lost_ap_sample_size = helper.getIntField(settings, "lostApSampleSize");
- params.min_breaching = helper.getIntField(settings, "minApsBreachingThreshold");
-
- const char *bssid_info_array_type = "[Landroid/net/wifi/WifiScanner$BssidInfo;";
- JNIObject<jobjectArray> bssids = helper.getArrayField(
- settings, "bssidInfos", bssid_info_array_type);
- params.num_bssid = helper.getArrayLength(bssids);
-
- if (params.num_bssid == 0) {
- ALOGE("BssidInfo array length was 0");
- return false;
- }
- if (params.num_bssid >
- static_cast<int>(sizeof(params.ap) / sizeof(params.ap[0]))) {
- ALOGE("trackSignificantWifiChange array length is too long");
- android_errorWriteLog(SAFE_NET_LOG_ID, "37775935");
- return false;
- }
- ALOGD("Initialized common fields %d, %d, %d, %d", params.rssi_sample_size,
- params.lost_ap_sample_size, params.min_breaching, params.num_bssid);
-
- for (int i = 0; i < params.num_bssid; i++) {
- JNIObject<jobject> objAp = helper.getObjectArrayElement(bssids, i);
-
- JNIObject<jstring> macAddrString = helper.getStringField(objAp, "bssid");
- if (macAddrString == NULL) {
- ALOGE("Error getting bssid field");
- return false;
- }
-
- ScopedUtfChars chars(env, macAddrString.get());
- const char *bssid = chars.c_str();
- if (bssid == NULL) {
- ALOGE("Error getting bssid");
- return false;
- }
-
- mac_addr addr;
- parseMacAddress(bssid, addr);
- memcpy(params.ap[i].bssid, addr, sizeof(mac_addr));
-
- char bssidOut[32];
- sprintf(bssidOut, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1],
- addr[2], addr[3], addr[4], addr[5]);
-
- params.ap[i].low = helper.getIntField(objAp, "low");
- params.ap[i].high = helper.getIntField(objAp, "high");
-
- ALOGD("Added bssid %s, [%04d, %04d]", bssidOut, params.ap[i].low, params.ap[i].high);
- }
-
- ALOGD("Added %d bssids", params.num_bssid);
-
- wifi_significant_change_handler handler;
- memset(&handler, 0, sizeof(handler));
-
- handler.on_significant_change = &onSignificantWifiChange;
- return hal_fn.wifi_set_significant_change_handler(id, handle, params, handler) == WIFI_SUCCESS;
-}
-
-static jboolean android_net_wifi_untrackSignificantWifiChange(
- JNIEnv *env, jclass cls, jint iface, jint id) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("resetting significant wifi change on interface[%d] = %p", iface, handle);
-
- return hal_fn.wifi_reset_significant_change_handler(id, handle) == WIFI_SUCCESS;
-}
-
-wifi_iface_stat link_stat;
-wifi_radio_stat radio_stat; // L release has support for only one radio
-u32 *tx_time_per_level_arr = 0;
-// Let's cache the supported feature set to avoid unnecessary HAL invocations.
-feature_set cached_feature_set = 0;
-
-bool isTxLevelStatsPresent(wifi_radio_stat *radio_stats) {
- if (IS_SUPPORTED_FEATURE(WIFI_FEATURE_TX_TRANSMIT_POWER, cached_feature_set)) {
- if(radio_stats->tx_time_per_levels != 0 && radio_stats->num_tx_levels > 0) {
- return true;
- } else {
- ALOGE("Ignoring invalid tx_level info in radio_stats");
- }
- }
- return false;
-}
-
-void onLinkStatsResults(wifi_request_id id, wifi_iface_stat *iface_stat,
- int num_radios, wifi_radio_stat *radio_stats)
-{
- if (iface_stat != 0) {
- memcpy(&link_stat, iface_stat, sizeof(wifi_iface_stat));
- } else {
- memset(&link_stat, 0, sizeof(wifi_iface_stat));
- }
-
- if (num_radios > 0 && radio_stats != 0) {
- memcpy(&radio_stat, radio_stats, sizeof(wifi_radio_stat));
- if (isTxLevelStatsPresent(radio_stats)) {
- // This realloc should be a no-op after the first allocation because for a given
- // device, the number of power levels should not change.
- u32 arr_size = sizeof(u32) * radio_stats->num_tx_levels;
- tx_time_per_level_arr = (u32 *)realloc(tx_time_per_level_arr, arr_size);
- memcpy(tx_time_per_level_arr, radio_stats->tx_time_per_levels, arr_size);
- radio_stat.tx_time_per_levels = tx_time_per_level_arr;
- } else {
- radio_stat.num_tx_levels = 0;
- radio_stat.tx_time_per_levels = 0;
- }
- } else {
- memset(&radio_stat, 0, sizeof(wifi_radio_stat));
- }
-}
-
-static void android_net_wifi_setLinkLayerStats (JNIEnv *env, jclass cls, jint iface, int enable) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
-
- wifi_link_layer_params params;
- params.aggressive_statistics_gathering = enable;
- params.mpdu_size_threshold = 128;
-
- ALOGD("android_net_wifi_setLinkLayerStats: %u\n", enable);
-
- hal_fn.wifi_set_link_stats(handle, params);
-}
-
-static jobject android_net_wifi_getLinkLayerStats (JNIEnv *env, jclass cls, jint iface) {
-
- JNIHelper helper(env);
- wifi_stats_result_handler handler;
- memset(&handler, 0, sizeof(handler));
- handler.on_link_stats_results = &onLinkStatsResults;
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- int result;
- // Cache the features supported by the device to determine if tx level stats are present or not
- if (cached_feature_set == 0) {
- result = hal_fn.wifi_get_supported_feature_set(handle, &cached_feature_set);
- if (result != WIFI_SUCCESS) {
- cached_feature_set = 0;
- }
- }
-
- result = hal_fn.wifi_get_link_stats(0, handle, handler);
- if (result < 0) {
- ALOGE("android_net_wifi_getLinkLayerStats: failed to get link statistics\n");
- return NULL;
- }
-
- JNIObject<jobject> wifiLinkLayerStats = helper.createObject(
- "android/net/wifi/WifiLinkLayerStats");
- if (wifiLinkLayerStats == NULL) {
- ALOGE("Error in allocating wifiLinkLayerStats");
- return NULL;
- }
-
- JNIObject<jintArray> tx_time_per_level = helper.newIntArray(radio_stat.num_tx_levels);
- if (tx_time_per_level == NULL) {
- ALOGE("Error in allocating wifiLinkLayerStats");
- return NULL;
- }
-
- helper.setIntField(wifiLinkLayerStats, "beacon_rx", link_stat.beacon_rx);
- helper.setIntField(wifiLinkLayerStats, "rssi_mgmt", link_stat.rssi_mgmt);
- helper.setLongField(wifiLinkLayerStats, "rxmpdu_be", link_stat.ac[WIFI_AC_BE].rx_mpdu);
- helper.setLongField(wifiLinkLayerStats, "rxmpdu_bk", link_stat.ac[WIFI_AC_BK].rx_mpdu);
- helper.setLongField(wifiLinkLayerStats, "rxmpdu_vi", link_stat.ac[WIFI_AC_VI].rx_mpdu);
- helper.setLongField(wifiLinkLayerStats, "rxmpdu_vo", link_stat.ac[WIFI_AC_VO].rx_mpdu);
- helper.setLongField(wifiLinkLayerStats, "txmpdu_be", link_stat.ac[WIFI_AC_BE].tx_mpdu);
- helper.setLongField(wifiLinkLayerStats, "txmpdu_bk", link_stat.ac[WIFI_AC_BK].tx_mpdu);
- helper.setLongField(wifiLinkLayerStats, "txmpdu_vi", link_stat.ac[WIFI_AC_VI].tx_mpdu);
- helper.setLongField(wifiLinkLayerStats, "txmpdu_vo", link_stat.ac[WIFI_AC_VO].tx_mpdu);
- helper.setLongField(wifiLinkLayerStats, "lostmpdu_be", link_stat.ac[WIFI_AC_BE].mpdu_lost);
- helper.setLongField(wifiLinkLayerStats, "lostmpdu_bk", link_stat.ac[WIFI_AC_BK].mpdu_lost);
- helper.setLongField(wifiLinkLayerStats, "lostmpdu_vi", link_stat.ac[WIFI_AC_VI].mpdu_lost);
- helper.setLongField(wifiLinkLayerStats, "lostmpdu_vo", link_stat.ac[WIFI_AC_VO].mpdu_lost);
- helper.setLongField(wifiLinkLayerStats, "retries_be", link_stat.ac[WIFI_AC_BE].retries);
- helper.setLongField(wifiLinkLayerStats, "retries_bk", link_stat.ac[WIFI_AC_BK].retries);
- helper.setLongField(wifiLinkLayerStats, "retries_vi", link_stat.ac[WIFI_AC_VI].retries);
- helper.setLongField(wifiLinkLayerStats, "retries_vo", link_stat.ac[WIFI_AC_VO].retries);
-
- helper.setIntField(wifiLinkLayerStats, "on_time", radio_stat.on_time);
- helper.setIntField(wifiLinkLayerStats, "tx_time", radio_stat.tx_time);
- helper.setIntField(wifiLinkLayerStats, "rx_time", radio_stat.rx_time);
- helper.setIntField(wifiLinkLayerStats, "on_time_scan", radio_stat.on_time_scan);
- if (radio_stat.tx_time_per_levels != 0) {
- helper.setIntArrayRegion(tx_time_per_level, 0, radio_stat.num_tx_levels,
- (jint *)radio_stat.tx_time_per_levels);
- }
- helper.setObjectField(wifiLinkLayerStats, "tx_time_per_level", "[I", tx_time_per_level);
-
-
- return wifiLinkLayerStats.detach();
-}
-
-static jint android_net_wifi_getSupportedFeatures(JNIEnv *env, jclass cls, jint iface) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- feature_set set = 0;
-
- wifi_error result = WIFI_SUCCESS;
- /*
- set = WIFI_FEATURE_INFRA
- | WIFI_FEATURE_INFRA_5G
- | WIFI_FEATURE_HOTSPOT
- | WIFI_FEATURE_P2P
- | WIFI_FEATURE_SOFT_AP
- | WIFI_FEATURE_GSCAN
- | WIFI_FEATURE_PNO
- | WIFI_FEATURE_TDLS
- | WIFI_FEATURE_EPR;
- */
-
- result = hal_fn.wifi_get_supported_feature_set(handle, &set);
- if (result == WIFI_SUCCESS) {
- // ALOGD("wifi_get_supported_feature_set returned set = 0x%x", set);
- return set;
- } else {
- ALOGE("wifi_get_supported_feature_set returned error = 0x%x", result);
- return 0;
- }
-}
-
-static void onRttResults(wifi_request_id id, unsigned num_results, wifi_rtt_result* results[]) {
-
- JNIHelper helper(mVM);
-
- if (DBG) ALOGD("onRttResults called, vm = %p, obj = %p", mVM, mCls);
-
- JNIObject<jobjectArray> rttResults = helper.newObjectArray(
- num_results, "android/net/wifi/RttManager$RttResult", NULL);
- if (rttResults == NULL) {
- ALOGE("Error in allocating RttResult array in onRttResults, length=%d", num_results);
- return;
- }
-
- for (unsigned i = 0; i < num_results; i++) {
-
- wifi_rtt_result *result = results[i];
-
- JNIObject<jobject> rttResult = helper.createObject("android/net/wifi/RttManager$RttResult");
- if (rttResult == NULL) {
- ALOGE("Error in creating rtt result in onRttResults");
- return;
- }
-
- char bssid[32];
- sprintf(bssid, "%02x:%02x:%02x:%02x:%02x:%02x", result->addr[0], result->addr[1],
- result->addr[2], result->addr[3], result->addr[4], result->addr[5]);
-
- helper.setStringField(rttResult, "bssid", bssid);
- helper.setIntField( rttResult, "burstNumber", result->burst_num);
- helper.setIntField( rttResult, "measurementFrameNumber", result->measurement_number);
- helper.setIntField( rttResult, "successMeasurementFrameNumber", result->success_number);
- helper.setIntField(rttResult, "frameNumberPerBurstPeer", result->number_per_burst_peer);
- helper.setIntField( rttResult, "status", result->status);
- helper.setIntField( rttResult, "measurementType", result->type);
- helper.setIntField(rttResult, "retryAfterDuration", result->retry_after_duration);
- helper.setLongField(rttResult, "ts", result->ts);
- helper.setIntField( rttResult, "rssi", result->rssi);
- helper.setIntField( rttResult, "rssiSpread", result->rssi_spread);
- helper.setIntField( rttResult, "txRate", result->tx_rate.bitrate);
- helper.setIntField( rttResult, "rxRate", result->rx_rate.bitrate);
- helper.setLongField(rttResult, "rtt", result->rtt);
- helper.setLongField(rttResult, "rttStandardDeviation", result->rtt_sd);
- helper.setIntField( rttResult, "distance", result->distance_mm / 10);
- helper.setIntField( rttResult, "distanceStandardDeviation", result->distance_sd_mm / 10);
- helper.setIntField( rttResult, "distanceSpread", result->distance_spread_mm / 10);
- helper.setIntField( rttResult, "burstDuration", result->burst_duration);
- helper.setIntField( rttResult, "negotiatedBurstNum", result->negotiated_burst_num);
-
- JNIObject<jobject> LCI = helper.createObject(
- "android/net/wifi/RttManager$WifiInformationElement");
- if (result->LCI != NULL && result->LCI->len > 0) {
- helper.setByteField(LCI, "id", result->LCI->id);
- JNIObject<jbyteArray> elements = helper.newByteArray(result->LCI->len);
- jbyte *bytes = (jbyte *)&(result->LCI->data[0]);
- helper.setByteArrayRegion(elements, 0, result->LCI->len, bytes);
- helper.setObjectField(LCI, "data", "[B", elements);
- } else {
- helper.setByteField(LCI, "id", (byte)(0xff));
- }
- helper.setObjectField(rttResult, "LCI",
- "Landroid/net/wifi/RttManager$WifiInformationElement;", LCI);
-
- JNIObject<jobject> LCR = helper.createObject(
- "android/net/wifi/RttManager$WifiInformationElement");
- if (result->LCR != NULL && result->LCR->len > 0) {
- helper.setByteField(LCR, "id", result->LCR->id);
- JNIObject<jbyteArray> elements = helper.newByteArray(result->LCI->len);
- jbyte *bytes = (jbyte *)&(result->LCR->data[0]);
- helper.setByteArrayRegion(elements, 0, result->LCI->len, bytes);
- helper.setObjectField(LCR, "data", "[B", elements);
- } else {
- helper.setByteField(LCR, "id", (byte)(0xff));
- }
- helper.setObjectField(rttResult, "LCR",
- "Landroid/net/wifi/RttManager$WifiInformationElement;", LCR);
-
- helper.setObjectArrayElement(rttResults, i, rttResult);
- }
-
- helper.reportEvent(mCls, "onRttResults", "(I[Landroid/net/wifi/RttManager$RttResult;)V",
- id, rttResults.get());
-}
-
-const int MaxRttConfigs = 16;
-
-static jboolean android_net_wifi_requestRange(
- JNIEnv *env, jclass cls, jint iface, jint id, jobject params) {
-
- JNIHelper helper(env);
-
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- if (DBG) ALOGD("sending rtt request [%d] = %p", id, handle);
- if (params == NULL) {
- ALOGE("ranging params are empty");
- return false;
- }
-
- wifi_rtt_config configs[MaxRttConfigs];
- memset(&configs, 0, sizeof(configs));
-
- int len = helper.getArrayLength((jobjectArray)params);
- if (len > MaxRttConfigs) {
- return false;
- }
-
- for (int i = 0; i < len; i++) {
-
- JNIObject<jobject> param = helper.getObjectArrayElement((jobjectArray)params, i);
- if (param == NULL) {
- ALOGW("could not get element %d", i);
- continue;
- }
-
- wifi_rtt_config &config = configs[i];
-
- parseMacAddress(env, param, config.addr);
- config.type = (wifi_rtt_type)helper.getIntField(param, "requestType");
- config.peer = (rtt_peer_type)helper.getIntField(param, "deviceType");
- config.channel.center_freq = helper.getIntField(param, "frequency");
- config.channel.width = (wifi_channel_width) helper.getIntField(param, "channelWidth");
- config.channel.center_freq0 = helper.getIntField(param, "centerFreq0");
- config.channel.center_freq1 = helper.getIntField(param, "centerFreq1");
-
- config.num_burst = helper.getIntField(param, "numberBurst");
- config.burst_period = (unsigned) helper.getIntField(param, "interval");
- config.num_frames_per_burst = (unsigned) helper.getIntField(param, "numSamplesPerBurst");
- config.num_retries_per_rtt_frame = (unsigned) helper.getIntField(param,
- "numRetriesPerMeasurementFrame");
- config.num_retries_per_ftmr = (unsigned) helper.getIntField(param, "numRetriesPerFTMR");
- config.LCI_request = helper.getBoolField(param, "LCIRequest") ? 1 : 0;
- config.LCR_request = helper.getBoolField(param, "LCRRequest") ? 1 : 0;
- config.burst_duration = (unsigned) helper.getIntField(param, "burstTimeout");
- config.preamble = (wifi_rtt_preamble) helper.getIntField(param, "preamble");
- config.bw = (wifi_rtt_bw) helper.getIntField(param, "bandwidth");
- }
-
- wifi_rtt_event_handler handler;
- handler.on_rtt_results = &onRttResults;
-
- return hal_fn.wifi_rtt_range_request(id, handle, len, configs, handler) == WIFI_SUCCESS;
-}
-
-static jboolean android_net_wifi_cancelRange(
- JNIEnv *env, jclass cls, jint iface, jint id, jobject params) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- if (DBG) ALOGD("cancelling rtt request [%d] = %p", id, handle);
-
- if (params == NULL) {
- ALOGE("ranging params are empty");
- return false;
- }
-
- mac_addr addrs[MaxRttConfigs];
- memset(&addrs, 0, sizeof(addrs));
-
- int len = helper.getArrayLength((jobjectArray)params);
- if (len > MaxRttConfigs) {
- return false;
- }
-
- for (int i = 0; i < len; i++) {
-
- JNIObject<jobject> param = helper.getObjectArrayElement(params, i);
- if (param == NULL) {
- ALOGW("could not get element %d", i);
- continue;
- }
-
- parseMacAddress(env, param, addrs[i]);
- }
-
- return hal_fn.wifi_rtt_range_cancel(id, handle, len, addrs) == WIFI_SUCCESS;
-}
-
-static jobject android_net_wifi_enableResponder(
- JNIEnv *env, jclass cls, jint iface, jint id, jint timeout_seconds, jobject channel_hint) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- if (DBG) ALOGD("enabling responder request [%d] = %p", id, handle);
- wifi_channel_info channel;
- // Get channel information from HAL if it's not provided by caller.
- if (channel_hint == NULL) {
- wifi_rtt_responder responder_info_hint;
- bool status = hal_fn.wifi_rtt_get_responder_info(handle, &responder_info_hint);
- if (status != WIFI_SUCCESS) {
- ALOGE("could not get available channel for responder");
- return NULL;
- }
- channel = responder_info_hint.channel;
- } else {
- channel.center_freq = helper.getIntField(channel_hint, "mPrimaryFrequency");
- channel.center_freq0 = helper.getIntField(channel_hint, "mCenterFrequency0");
- channel.center_freq1 = helper.getIntField(channel_hint, "mCenterFrequency1");
- channel.width = (wifi_channel_width)helper.getIntField(channel_hint, "mChannelWidth");
- }
-
- if (DBG) {
- ALOGD("wifi_channel_width: %d, center_freq: %d, center_freq0: %d",
- channel.width, channel.center_freq, channel.center_freq0);
- }
-
- wifi_rtt_responder responder_info_used;
- bool status = hal_fn.wifi_enable_responder(id, handle, channel, timeout_seconds,
- &responder_info_used);
- if (status != WIFI_SUCCESS) {
- ALOGE("enabling responder mode failed");
- return NULL;
- }
- wifi_channel_info channel_used = responder_info_used.channel;
- if (DBG) {
- ALOGD("wifi_channel_width: %d, center_freq: %d, center_freq0: %d",
- channel_used.width, channel_used.center_freq, channel_used.center_freq0);
- }
- JNIObject<jobject> responderConfig =
- helper.createObject("android/net/wifi/RttManager$ResponderConfig");
- if (responderConfig == NULL) return NULL;
- helper.setIntField(responderConfig, "frequency", channel_used.center_freq);
- helper.setIntField(responderConfig, "centerFreq0", channel_used.center_freq0);
- helper.setIntField(responderConfig, "centerFreq1", channel_used.center_freq1);
- helper.setIntField(responderConfig, "channelWidth", channel_used.width);
- helper.setIntField(responderConfig, "preamble", responder_info_used.preamble);
- return responderConfig.detach();
-}
-
-static jboolean android_net_wifi_disableResponder(
- JNIEnv *env, jclass cls, jint iface, jint id) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- if (DBG) ALOGD("disabling responder request [%d] = %p", id, handle);
- return hal_fn.wifi_disable_responder(id, handle) == WIFI_SUCCESS;
-}
-
-
-static jboolean android_net_wifi_setScanningMacOui(JNIEnv *env, jclass cls,
- jint iface, jbyteArray param) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("setting scan oui %p", handle);
-
- static const unsigned oui_len = 3; /* OUI is upper 3 bytes of mac_address */
- int len = helper.getArrayLength(param);
- if (len != oui_len) {
- ALOGE("invalid oui length %d", len);
- return false;
- }
-
- ScopedBytesRW paramBytes(env, param);
- jbyte* bytes = paramBytes.get();
- if (bytes == NULL) {
- ALOGE("failed to get setScanningMacOui param array");
- return false;
- }
-
- return hal_fn.wifi_set_scanning_mac_oui(handle, (byte *)bytes) == WIFI_SUCCESS;
-}
-
-static jboolean android_net_wifi_is_get_channels_for_band_supported(JNIEnv *env, jclass cls){
- return (hal_fn.wifi_get_valid_channels == wifi_get_valid_channels_stub);
-}
-
-static jintArray android_net_wifi_getValidChannels(JNIEnv *env, jclass cls,
- jint iface, jint band) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGV("getting valid channels %p", handle);
-
- static const int MaxChannels = 64;
- wifi_channel channels[64];
- int num_channels = 0;
- wifi_error result = hal_fn.wifi_get_valid_channels(handle, band, MaxChannels,
- channels, &num_channels);
-
- if (result == WIFI_SUCCESS) {
- JNIObject<jintArray> channelArray = helper.newIntArray(num_channels);
- if (channelArray == NULL) {
- ALOGE("failed to allocate channel list, num_channels=%d", num_channels);
- return NULL;
- }
-
- helper.setIntArrayRegion(channelArray, 0, num_channels, channels);
- return channelArray.detach();
- } else {
- ALOGE("failed to get channel list : %d", result);
- return NULL;
- }
-}
-
-static jboolean android_net_wifi_setDfsFlag(JNIEnv *env, jclass cls, jint iface, jboolean dfs) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("setting dfs flag to %s, %p", dfs ? "true" : "false", handle);
-
- u32 nodfs = dfs ? 0 : 1;
- wifi_error result = hal_fn.wifi_set_nodfs_flag(handle, nodfs);
- return result == WIFI_SUCCESS;
-}
-
-static jobject android_net_wifi_get_rtt_capabilities(JNIEnv *env, jclass cls, jint iface) {
-
- JNIHelper helper(env);
- wifi_rtt_capabilities rtt_capabilities;
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- wifi_error ret = hal_fn.wifi_get_rtt_capabilities(handle, &rtt_capabilities);
-
- if(WIFI_SUCCESS == ret) {
- JNIObject<jobject> capabilities = helper.createObject(
- "android/net/wifi/RttManager$RttCapabilities");
- helper.setBooleanField(capabilities, "oneSidedRttSupported",
- rtt_capabilities.rtt_one_sided_supported == 1);
- helper.setBooleanField(capabilities, "twoSided11McRttSupported",
- rtt_capabilities.rtt_ftm_supported == 1);
- helper.setBooleanField(capabilities, "lciSupported",
- rtt_capabilities.lci_support);
- helper.setBooleanField(capabilities, "lcrSupported",
- rtt_capabilities.lcr_support);
- helper.setIntField(capabilities, "preambleSupported",
- rtt_capabilities.preamble_support);
- helper.setIntField(capabilities, "bwSupported",
- rtt_capabilities.bw_support);
- helper.setBooleanField(capabilities, "responderSupported",
- rtt_capabilities.responder_supported == 1);
- if (DBG) {
- ALOGD("One side RTT is %s", rtt_capabilities.rtt_one_sided_supported == 1 ?
- "supported" : "not supported");
- ALOGD("Two side RTT is %s", rtt_capabilities.rtt_ftm_supported == 1 ?
- "supported" : "not supported");
- ALOGD("LCR is %s", rtt_capabilities.lcr_support == 1 ? "supported" : "not supported");
- ALOGD("LCI is %s", rtt_capabilities.lci_support == 1 ? "supported" : "not supported");
- ALOGD("Supported preamble is %d", rtt_capabilities.preamble_support);
- ALOGD("Supported bandwidth is %d", rtt_capabilities.bw_support);
- ALOGD("Sta responder is %s",
- rtt_capabilities.responder_supported == 1 ? "supported" : "not supported");
- }
- return capabilities.detach();
- } else {
- return NULL;
- }
-}
-
-static jobject android_net_wifi_get_apf_capabilities(JNIEnv *env, jclass cls,
- jint iface) {
-
- JNIHelper helper(env);
- u32 version = 0, max_len = 0;
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- wifi_error ret = hal_fn.wifi_get_packet_filter_capabilities(handle, &version, &max_len);
-
- if (WIFI_SUCCESS == ret) {
- // Cannot just use createObject() because members are final and initializer values must be
- // passed via ApfCapabilities().
- JNIObject<jclass> apf_cls(helper, env->FindClass("android/net/apf/ApfCapabilities"));
- if (apf_cls == NULL) {
- ALOGE("Error in finding class android/net/apf/ApfCapabilities");
- return NULL;
- }
- jmethodID constructor = env->GetMethodID(apf_cls, "<init>", "(III)V");
- if (constructor == 0) {
- ALOGE("Error in constructor ID for android/net/apf/ApfCapabilities");
- return NULL;
- }
- JNIObject<jobject> capabilities(helper, env->NewObject(apf_cls, constructor, version,
- max_len, ARPHRD_ETHER));
- if (capabilities == NULL) {
- ALOGE("Could not create new object of android/net/apf/ApfCapabilities");
- return NULL;
- }
- ALOGD("APF version supported: %d", version);
- ALOGD("Maximum APF program size: %d", max_len);
- return capabilities.detach();
- } else {
- return NULL;
- }
-}
-
-static jboolean android_net_wifi_install_packet_filter(JNIEnv *env, jclass cls, jint iface,
- jbyteArray jfilter) {
-
- JNIHelper helper(env);
- const u8* filter = (uint8_t*)env->GetByteArrayElements(jfilter, NULL);
- const u32 filter_len = env->GetArrayLength(jfilter);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- wifi_error ret = hal_fn.wifi_set_packet_filter(handle, filter, filter_len);
- env->ReleaseByteArrayElements(jfilter, (jbyte*)filter, JNI_ABORT);
- return WIFI_SUCCESS == ret;
-}
-
-static jboolean android_net_wifi_set_Country_Code_Hal(JNIEnv *env,jclass cls, jint iface,
- jstring country_code) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
-
- ScopedUtfChars chars(env, country_code);
- const char *country = chars.c_str();
-
- ALOGD("set country code: %s", country);
- wifi_error res = hal_fn.wifi_set_country_code(handle, country);
- return res == WIFI_SUCCESS;
-}
-
-static jboolean android_net_wifi_enable_disable_tdls(JNIEnv *env,jclass cls, jint iface,
- jboolean enable, jstring addr) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
-
- mac_addr address;
- parseMacAddress(env, addr, address);
- wifi_tdls_handler tdls_handler;
- //tdls_handler.on_tdls_state_changed = &on_tdls_state_changed;
-
- if(enable) {
- return (hal_fn.wifi_enable_tdls(handle, address, NULL, tdls_handler) == WIFI_SUCCESS);
- } else {
- return (hal_fn.wifi_disable_tdls(handle, address) == WIFI_SUCCESS);
- }
-}
-
-static void on_tdls_state_changed(mac_addr addr, wifi_tdls_status status) {
-
- JNIHelper helper(mVM);
-
- ALOGD("on_tdls_state_changed is called: vm = %p, obj = %p", mVM, mCls);
-
- char mac[32];
- sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4],
- addr[5]);
-
- JNIObject<jstring> mac_address = helper.newStringUTF(mac);
- helper.reportEvent(mCls, "onTdlsStatus", "(Ljava/lang/StringII;)V",
- mac_address.get(), status.state, status.reason);
-
-}
-
-static jobject android_net_wifi_get_tdls_status(JNIEnv *env,jclass cls, jint iface,jstring addr) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
-
- mac_addr address;
- parseMacAddress(env, addr, address);
-
- wifi_tdls_status status;
-
- wifi_error ret;
- ret = hal_fn.wifi_get_tdls_status(handle, address, &status );
-
- if (ret != WIFI_SUCCESS) {
- return NULL;
- } else {
- JNIObject<jobject> tdls_status = helper.createObject(
- "com/android/server/wifi/WifiNative$TdlsStatus");
- helper.setIntField(tdls_status, "channel", status.channel);
- helper.setIntField(tdls_status, "global_operating_class", status.global_operating_class);
- helper.setIntField(tdls_status, "state", status.state);
- helper.setIntField(tdls_status, "reason", status.reason);
- return tdls_status.detach();
- }
-}
-
-static jobject android_net_wifi_get_tdls_capabilities(JNIEnv *env, jclass cls, jint iface) {
-
- JNIHelper helper(env);
- wifi_tdls_capabilities tdls_capabilities;
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- wifi_error ret = hal_fn.wifi_get_tdls_capabilities(handle, &tdls_capabilities);
-
- if (WIFI_SUCCESS == ret) {
- JNIObject<jobject> capabilities = helper.createObject(
- "com/android/server/wifi/WifiNative$TdlsCapabilities");
- helper.setIntField(capabilities, "maxConcurrentTdlsSessionNumber",
- tdls_capabilities.max_concurrent_tdls_session_num);
- helper.setBooleanField(capabilities, "isGlobalTdlsSupported",
- tdls_capabilities.is_global_tdls_supported == 1);
- helper.setBooleanField(capabilities, "isPerMacTdlsSupported",
- tdls_capabilities.is_per_mac_tdls_supported == 1);
- helper.setBooleanField(capabilities, "isOffChannelTdlsSupported",
- tdls_capabilities.is_off_channel_tdls_supported);
-
- ALOGD("TDLS Max Concurrent Tdls Session Number is: %d",
- tdls_capabilities.max_concurrent_tdls_session_num);
- ALOGD("Global Tdls is: %s", tdls_capabilities.is_global_tdls_supported == 1 ? "support" :
- "not support");
- ALOGD("Per Mac Tdls is: %s", tdls_capabilities.is_per_mac_tdls_supported == 1 ? "support" :
- "not support");
- ALOGD("Off Channel Tdls is: %s", tdls_capabilities.is_off_channel_tdls_supported == 1 ?
- "support" : "not support");
-
- return capabilities.detach();
- } else {
- return NULL;
- }
-}
-
-// ----------------------------------------------------------------------------
-// Debug framework
-// ----------------------------------------------------------------------------
-static jint android_net_wifi_get_supported_logger_feature(JNIEnv *env, jclass cls, jint iface){
- //Not implemented yet
- return -1;
-}
-
-static jobject android_net_wifi_get_driver_version(JNIEnv *env, jclass cls, jint iface) {
- //Need to be fixed. The memory should be allocated from lower layer
- //char *buffer = NULL;
- JNIHelper helper(env);
- int buffer_length = 256;
- char *buffer = (char *)malloc(buffer_length);
- if (!buffer) return NULL;
- memset(buffer, 0, buffer_length);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
-
- ALOGD("android_net_wifi_get_driver_version = %p", handle);
-
- if (handle == 0) {
- free(buffer);
- return NULL;
- }
-
- wifi_error result = hal_fn.wifi_get_driver_version(handle, buffer, buffer_length);
-
- if (result == WIFI_SUCCESS) {
- ALOGD("buffer is %p, length is %d", buffer, buffer_length);
- JNIObject<jstring> driver_version = helper.newStringUTF(buffer);
- free(buffer);
- return driver_version.detach();
- } else {
- ALOGE("Fail to get driver version");
- free(buffer);
- return NULL;
- }
-}
-
-static jobject android_net_wifi_get_firmware_version(JNIEnv *env, jclass cls, jint iface) {
-
- //char *buffer = NULL;
- JNIHelper helper(env);
- int buffer_length = 256;
- char *buffer = (char *)malloc(buffer_length);
- if (!buffer) return NULL;
- memset(buffer, 0, buffer_length);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
-
- ALOGD("android_net_wifi_get_firmware_version = %p", handle);
-
- if (handle == 0) {
- free(buffer);
- return NULL;
- }
-
- wifi_error result = hal_fn.wifi_get_firmware_version(handle, buffer, buffer_length);
-
- if (result == WIFI_SUCCESS) {
- ALOGD("buffer is %p, length is %d", buffer, buffer_length);
- JNIObject<jstring> firmware_version = helper.newStringUTF(buffer);
- free(buffer);
- return firmware_version.detach();
- } else {
- ALOGE("Fail to get Firmware version");
- free(buffer);
- return NULL;
- }
-}
-
-static jobject android_net_wifi_get_ring_buffer_status (JNIEnv *env, jclass cls, jint iface) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
-
- ALOGD("android_net_wifi_get_ring_buffer_status = %p", handle);
-
- if (handle == 0) {
- return NULL;
- }
-
- //wifi_ring_buffer_status *status = NULL;
- u32 num_rings = 10;
- wifi_ring_buffer_status *status =
- (wifi_ring_buffer_status *)malloc(sizeof(wifi_ring_buffer_status) * num_rings);
- if (!status) return NULL;
- memset(status, 0, sizeof(wifi_ring_buffer_status) * num_rings);
- wifi_error result = hal_fn.wifi_get_ring_buffers_status(handle, &num_rings, status);
- if (result == WIFI_SUCCESS) {
- ALOGD("status is %p, number is %d", status, num_rings);
-
- JNIObject<jobjectArray> ringBuffersStatus = helper.newObjectArray(
- num_rings, "com/android/server/wifi/WifiNative$RingBufferStatus", NULL);
-
- wifi_ring_buffer_status *tmp = status;
-
- for(u32 i = 0; i < num_rings; i++, tmp++) {
-
- JNIObject<jobject> ringStatus = helper.createObject(
- "com/android/server/wifi/WifiNative$RingBufferStatus");
-
- if (ringStatus == NULL) {
- ALOGE("Error in creating ringBufferStatus");
- free(status);
- return NULL;
- }
-
- char name[32];
- for(int j = 0; j < 32; j++) {
- name[j] = tmp->name[j];
- }
-
- helper.setStringField(ringStatus, "name", name);
- helper.setIntField(ringStatus, "flag", tmp->flags);
- helper.setIntField(ringStatus, "ringBufferId", tmp->ring_id);
- helper.setIntField(ringStatus, "ringBufferByteSize", tmp->ring_buffer_byte_size);
- helper.setIntField(ringStatus, "verboseLevel", tmp->verbose_level);
- helper.setIntField(ringStatus, "writtenBytes", tmp->written_bytes);
- helper.setIntField(ringStatus, "readBytes", tmp->read_bytes);
- helper.setIntField(ringStatus, "writtenRecords", tmp->written_records);
-
- helper.setObjectArrayElement(ringBuffersStatus, i, ringStatus);
- }
-
- free(status);
- return ringBuffersStatus.detach();
- } else {
- free(status);
- return NULL;
- }
-}
-
-static void on_ring_buffer_data(char *ring_name, char *buffer, int buffer_size,
- wifi_ring_buffer_status *status) {
-
- if (!ring_name || !buffer || !status ||
- (unsigned int)buffer_size <= sizeof(wifi_ring_buffer_entry)) {
- ALOGE("Error input for on_ring_buffer_data!");
- return;
- }
-
-
- JNIHelper helper(mVM);
- /* ALOGD("on_ring_buffer_data called, vm = %p, obj = %p, env = %p buffer size = %d", mVM,
- mCls, env, buffer_size); */
-
- JNIObject<jobject> ringStatus = helper.createObject(
- "com/android/server/wifi/WifiNative$RingBufferStatus");
- if (status == NULL) {
- ALOGE("Error in creating ringBufferStatus");
- return;
- }
-
- helper.setStringField(ringStatus, "name", ring_name);
- helper.setIntField(ringStatus, "flag", status->flags);
- helper.setIntField(ringStatus, "ringBufferId", status->ring_id);
- helper.setIntField(ringStatus, "ringBufferByteSize", status->ring_buffer_byte_size);
- helper.setIntField(ringStatus, "verboseLevel", status->verbose_level);
- helper.setIntField(ringStatus, "writtenBytes", status->written_bytes);
- helper.setIntField(ringStatus, "readBytes", status->read_bytes);
- helper.setIntField(ringStatus, "writtenRecords", status->written_records);
-
- JNIObject<jbyteArray> bytes = helper.newByteArray(buffer_size);
- helper.setByteArrayRegion(bytes, 0, buffer_size, (jbyte*)buffer);
-
- helper.reportEvent(mCls,"onRingBufferData",
- "(Lcom/android/server/wifi/WifiNative$RingBufferStatus;[B)V",
- ringStatus.get(), bytes.get());
-}
-
-static void on_alert_data(wifi_request_id id, char *buffer, int buffer_size, int err_code){
-
- JNIHelper helper(mVM);
- ALOGD("on_alert_data called, vm = %p, obj = %p, buffer_size = %d, error code = %d"
- , mVM, mCls, buffer_size, err_code);
-
- if (buffer_size > 0) {
- JNIObject<jbyteArray> records = helper.newByteArray(buffer_size);
- jbyte *bytes = (jbyte *) buffer;
- helper.setByteArrayRegion(records, 0,buffer_size, bytes);
- helper.reportEvent(mCls,"onWifiAlert","([BI)V", records.get(), err_code);
- } else {
- helper.reportEvent(mCls,"onWifiAlert","([BI)V", NULL, err_code);
- }
-}
-
-
-static jboolean android_net_wifi_start_logging_ring_buffer(JNIEnv *env, jclass cls, jint iface,
- jint verbose_level,jint flags, jint max_interval,jint min_data_size, jstring ring_name) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
-
- ALOGD("android_net_wifi_start_logging_ring_buffer = %p", handle);
-
- if (handle == 0) {
- return false;
- }
-
- ScopedUtfChars chars(env, ring_name);
- const char* ring_name_const_char = chars.c_str();
- int ret = hal_fn.wifi_start_logging(handle, verbose_level,
- flags, max_interval, min_data_size, const_cast<char *>(ring_name_const_char));
-
- if (ret != WIFI_SUCCESS) {
- ALOGE("Fail to start logging for ring %s", ring_name_const_char);
- } else {
- ALOGD("start logging for ring %s", ring_name_const_char);
- }
-
- return ret == WIFI_SUCCESS;
-}
-
-static jboolean android_net_wifi_get_ring_buffer_data(JNIEnv *env, jclass cls, jint iface,
- jstring ring_name) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- // ALOGD("android_net_wifi_get_ring_buffer_data = %p", handle);
-
- ScopedUtfChars chars(env, ring_name);
- const char* ring_name_const_char = chars.c_str();
- int result = hal_fn.wifi_get_ring_data(handle, const_cast<char *>(ring_name_const_char));
- return result == WIFI_SUCCESS;
-}
-
-
-static void on_firmware_memory_dump(char *buffer, int buffer_size) {
-
- JNIHelper helper(mVM);
- /* ALOGD("on_firmware_memory_dump called, vm = %p, obj = %p, env = %p buffer_size = %d"
- , mVM, mCls, env, buffer_size); */
-
- if (buffer_size > 0) {
- JNIObject<jbyteArray> dump = helper.newByteArray(buffer_size);
- jbyte *bytes = (jbyte *) (buffer);
- helper.setByteArrayRegion(dump, 0, buffer_size, bytes);
- helper.reportEvent(mCls,"onWifiFwMemoryAvailable","([B)V", dump.get());
- }
-}
-
-static jboolean android_net_wifi_get_fw_memory_dump(JNIEnv *env, jclass cls, jint iface){
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- // ALOGD("android_net_wifi_get_fw_memory_dump = %p", handle);
-
- if (handle == NULL) {
- ALOGE("Can not get wifi_interface_handle");
- return false;
- }
-
- wifi_firmware_memory_dump_handler fw_dump_handle;
- fw_dump_handle.on_firmware_memory_dump = on_firmware_memory_dump;
- int result = hal_fn.wifi_get_firmware_memory_dump(handle, fw_dump_handle);
- return result == WIFI_SUCCESS;
-
-}
-
-std::vector<jbyte>* driver_state_dump_buffer_for_callback = nullptr;
-
-static void on_driver_state_dump(char *buffer, int buffer_size);
-static wifi_driver_memory_dump_callbacks driver_state_dump_callbacks = {
- on_driver_state_dump
-};
-
-static void on_driver_state_dump(char *buffer, int buffer_size) {
-
- if (!driver_state_dump_buffer_for_callback) {
- ALOGE("Unexpected call from HAL implementation, into %s", __func__);
- return;
- }
-
- if (buffer_size > 0) {
- driver_state_dump_buffer_for_callback->insert(
- driver_state_dump_buffer_for_callback->end(), buffer, buffer + buffer_size);
- }
-}
-
-// TODO(quiche): Add unit tests. b/28072392
-static jbyteArray android_net_wifi_get_driver_state_dump(JNIEnv *env, jclass cls, jint iface){
-
- JNIHelper helper(env);
- wifi_interface_handle interface_handle = getIfaceHandle(helper, cls, iface);
-
- if (!interface_handle) {
- return nullptr;
- }
-
- int result;
- std::vector<jbyte> state_dump_buffer_local;
- driver_state_dump_buffer_for_callback = &state_dump_buffer_local;
- result = hal_fn.wifi_get_driver_memory_dump(interface_handle, driver_state_dump_callbacks);
- driver_state_dump_buffer_for_callback = nullptr;
-
- if (result != WIFI_SUCCESS) {
- ALOGW("HAL's wifi_get_driver_memory_dump returned %d", result);
- return nullptr;
- }
-
- if (state_dump_buffer_local.empty()) {
- ALOGW("HAL's wifi_get_driver_memory_dump provided zero bytes");
- return nullptr;
- }
-
- const size_t dump_size = state_dump_buffer_local.size();
- JNIObject<jbyteArray> driver_dump_java = helper.newByteArray(dump_size);
- if (!driver_dump_java) {
- ALOGW("Failed to allocate Java buffer for driver state dump");
- return nullptr;
- }
-
- helper.setByteArrayRegion(driver_dump_java, 0, dump_size, state_dump_buffer_local.data());
- return driver_dump_java.detach();
-}
-
-static jboolean android_net_wifi_set_log_handler(JNIEnv *env, jclass cls, jint iface, jint id) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("android_net_wifi_set_log_handler = %p", handle);
-
- //initialize the handler on first time
- wifi_ring_buffer_data_handler handler;
- handler.on_ring_buffer_data = &on_ring_buffer_data;
- int result = hal_fn.wifi_set_log_handler(id, handle, handler);
- if (result != WIFI_SUCCESS) {
- ALOGE("Fail to set logging handler");
- return false;
- }
-
- //set alter handler This will start alert too
- wifi_alert_handler alert_handler;
- alert_handler.on_alert = &on_alert_data;
- result = hal_fn.wifi_set_alert_handler(id, handle, alert_handler);
- if (result != WIFI_SUCCESS) {
- ALOGE(" Fail to set alert handler");
- return false;
- }
-
- return true;
-}
-
-static jboolean android_net_wifi_reset_log_handler(JNIEnv *env, jclass cls, jint iface, jint id) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
-
- //reset alter handler
- ALOGD("android_net_wifi_reset_alert_handler = %p", handle);
- int result = hal_fn.wifi_reset_alert_handler(id, handle);
- if (result != WIFI_SUCCESS) {
- ALOGE(" Fail to reset alert handler");
- return false;
- }
-
- //reset log handler
- ALOGD("android_net_wifi_reset_log_handler = %p", handle);
- result = hal_fn.wifi_reset_log_handler(id, handle);
- if (result != WIFI_SUCCESS) {
- ALOGE("Fail to reset logging handler");
- return false;
- }
-
- return true;
-}
-
-static jint android_net_wifi_start_pkt_fate_monitoring(JNIEnv *env, jclass cls, jint iface) {
-
- JNIHelper helper(env);
- return hal_fn.wifi_start_pkt_fate_monitoring(
- getIfaceHandle(helper, cls, iface));
-}
-
-// Helper for make_default_fate().
-template<typename T> void set_to_max(T* value) {
- if (!value) {
- return;
- }
- *value = std::numeric_limits<T>::max();
-}
-
-// make_default_fate() has two purposes:
-// 1) Minimize the chances of data leakage. In case the HAL gives us an overlong long |frame_len|,
-// for example, we want to return zeros, rather than other data from this process.
-// 2) Make it obvious when the HAL doesn't set a field. We accomplish this by setting fields
-// to "impossible" values, where possible.
-// Normally, such work would be done in a ctor. However, doing so would make the HAL API
-// incompatible with C. So we use a free-standing function instead.
-//
-// TODO(quiche): Add unit test for this function. b/27726696
-template<typename FateReportT> FateReportT make_default_fate() {
-
- FateReportT fate_report;
- set_to_max(&fate_report.fate);
- std::fill(std::begin(fate_report.md5_prefix), std::end(fate_report.md5_prefix), 0);
- set_to_max(&fate_report.frame_inf.payload_type);
- fate_report.frame_inf.frame_len = 0;
- fate_report.frame_inf.driver_timestamp_usec = 0;
- fate_report.frame_inf.firmware_timestamp_usec = 0;
- std::fill(std::begin(fate_report.frame_inf.frame_content.ieee_80211_mgmt_bytes),
- std::end(fate_report.frame_inf.frame_content.ieee_80211_mgmt_bytes), 0);
- return fate_report;
-}
-
-// TODO(quiche): Add unit test for this function. b/27726696
-template<typename FateReportT, typename HalFateFetcherT> wifi_error get_pkt_fates(
- HalFateFetcherT fate_fetcher_func, const char *java_fate_type,
- JNIEnv *env, jclass cls, jint iface, jobjectArray reports) {
-
- JNIHelper helper(env);
- const size_t n_reports_wanted =
- std::min(helper.getArrayLength(reports), MAX_FATE_LOG_LEN);
-
- std::vector<FateReportT> report_bufs(n_reports_wanted, make_default_fate<FateReportT>());
- size_t n_reports_provided = 0;
- wifi_error result = fate_fetcher_func(
- getIfaceHandle(helper, cls, iface),
- report_bufs.data(),
- n_reports_wanted,
- &n_reports_provided);
- if (result != WIFI_SUCCESS) {
- return result;
- }
-
- if (n_reports_provided > n_reports_wanted) {
- LOG_ALWAYS_FATAL(
- "HAL data exceeds request; memory may be corrupt (provided: %zu, requested: %zu)",
- n_reports_provided, n_reports_wanted);
- }
-
- for (size_t i = 0; i < n_reports_provided; ++i) {
- const FateReportT& report(report_bufs[i]);
-
- const char *frame_bytes_native = nullptr;
- size_t max_frame_len;
- switch (report.frame_inf.payload_type) {
- case FRAME_TYPE_UNKNOWN:
- case FRAME_TYPE_ETHERNET_II:
- max_frame_len = MAX_FRAME_LEN_ETHERNET;
- frame_bytes_native = report.frame_inf.frame_content.ethernet_ii_bytes;
- break;
- case FRAME_TYPE_80211_MGMT:
- max_frame_len = MAX_FRAME_LEN_80211_MGMT;
- frame_bytes_native = report.frame_inf.frame_content.ieee_80211_mgmt_bytes;
- break;
- default:
- max_frame_len = 0;
- frame_bytes_native = 0;
- }
-
- size_t copy_len = report.frame_inf.frame_len;
- if (copy_len > max_frame_len) {
- ALOGW("Overly long frame (len: %zu, max: %zu)", copy_len, max_frame_len);
- copy_len = max_frame_len;
- }
-
- JNIObject<jbyteArray> frame_bytes_java = helper.newByteArray(copy_len);
- if (frame_bytes_java.isNull()) {
- ALOGE("Failed to allocate frame data buffer");
- return WIFI_ERROR_OUT_OF_MEMORY;
- }
- helper.setByteArrayRegion(frame_bytes_java, 0, copy_len,
- reinterpret_cast<const jbyte *>(frame_bytes_native));
-
- JNIObject<jobject> fate_report = helper.createObjectWithArgs(
- java_fate_type,
- "(BJB[B)V", // byte, long, byte, byte array
- static_cast<jbyte>(report.fate),
- static_cast<jlong>(report.frame_inf.driver_timestamp_usec),
- static_cast<jbyte>(report.frame_inf.payload_type),
- frame_bytes_java.get());
- if (fate_report.isNull()) {
- ALOGE("Failed to create %s", java_fate_type);
- return WIFI_ERROR_OUT_OF_MEMORY;
- }
- helper.setObjectArrayElement(reports, i, fate_report);
- }
-
- return result;
-}
-
-static jint android_net_wifi_get_tx_pkt_fates(JNIEnv *env, jclass cls, jint iface,
- jobjectArray reports) {
-
- return get_pkt_fates<wifi_tx_report>(
- hal_fn.wifi_get_tx_pkt_fates, "com/android/server/wifi/WifiNative$TxFateReport",
- env, cls, iface, reports);
-}
-
-static jint android_net_wifi_get_rx_pkt_fates(JNIEnv *env, jclass cls, jint iface,
- jobjectArray reports) {
-
- return get_pkt_fates<wifi_rx_report>(
- hal_fn.wifi_get_rx_pkt_fates, "com/android/server/wifi/WifiNative$RxFateReport",
- env, cls, iface, reports);
-}
-
-// ----------------------------------------------------------------------------
-// ePno framework
-// ----------------------------------------------------------------------------
-
-
-static void onPnoNetworkFound(wifi_request_id id,
- unsigned num_results, wifi_scan_result *results) {
- JNIHelper helper(mVM);
- ALOGD("onPnoNetworkFound called, vm = %p, obj = %p, num_results %u", mVM, mCls, num_results);
-
- if (results == NULL || num_results == 0) {
- ALOGE("onPnoNetworkFound: Error no results");
- return;
- }
-
- JNIObject<jobjectArray> scanResults = helper.newObjectArray(num_results,
- "android/net/wifi/ScanResult", NULL);
- if (scanResults == NULL) {
- ALOGE("onpnoNetworkFound: Error in allocating scanResults array");
- return;
- }
-
- JNIObject<jintArray> beaconCaps = helper.newIntArray(num_results);
- if (beaconCaps == NULL) {
- ALOGE("onpnoNetworkFound: Error in allocating beaconCaps array");
- return;
- }
-
- for (unsigned i=0; i<num_results; i++) {
-
- JNIObject<jobject> scanResult = createScanResult(helper, &results[i], true);
- if (scanResult == NULL) {
- ALOGE("Error in creating scan result");
- return;
- }
-
- helper.setObjectArrayElement(scanResults, i, scanResult);
- helper.setIntArrayRegion(beaconCaps, i, 1, (jint *)&(results[i].capability));
-
- if (DBG) {
- ALOGD("ScanResult: IE length %d, i %u, <%s> rssi=%d %02x:%02x:%02x:%02x:%02x:%02x",
- results->ie_length, i, results[i].ssid, results[i].rssi,
- results[i].bssid[0], results[i].bssid[1],results[i].bssid[2],
- results[i].bssid[3], results[i].bssid[4], results[i].bssid[5]);
- }
- }
-
- helper.reportEvent(mCls, "onPnoNetworkFound", "(I[Landroid/net/wifi/ScanResult;[I)V", id,
- scanResults.get(), beaconCaps.get());
-}
-
-static jboolean android_net_wifi_setPnoListNative(
- JNIEnv *env, jclass cls, jint iface, jint id, jobject settings) {
-
- JNIHelper helper(env);
- wifi_epno_handler handler;
- handler.on_network_found = &onPnoNetworkFound;
-
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("configure ePno list request [%d] = %p", id, handle);
-
- if (settings == NULL) {
- return false;
- }
-
- JNIObject<jobjectArray> list = helper.getArrayField(settings, "networkList",
- "[Lcom/android/server/wifi/WifiNative$PnoNetwork;");
- if (list == NULL) {
- return false;
- }
-
- size_t len = helper.getArrayLength(list);
- if (len > (size_t)MAX_EPNO_NETWORKS) {
- return false;
- }
-
- wifi_epno_params params;
- memset(¶ms, 0, sizeof(params));
-
- for (unsigned int i = 0; i < len; i++) {
-
- JNIObject<jobject> pno_net = helper.getObjectArrayElement(list, i);
- if (pno_net == NULL) {
- ALOGE("setPnoListNative: could not get element %d", i);
- continue;
- }
-
- JNIObject<jstring> sssid = helper.getStringField(pno_net, "ssid");
- if (sssid == NULL) {
- ALOGE("Error setPnoListNative: getting ssid field");
- return false;
- }
-
- ScopedUtfChars chars(env, (jstring)sssid.get());
- const char *ssid = chars.c_str();
- if (ssid == NULL) {
- ALOGE("Error setPnoListNative: getting ssid");
- return false;
- }
- int ssid_len = strnlen((const char*)ssid, 33);
- if (ssid_len > 32) {
- ALOGE("Error setPnoListNative: long ssid %zu", strnlen((const char*)ssid, 256));
- return false;
- }
-
- if (ssid_len > 1 && ssid[0] == '"' && ssid[ssid_len-1] == '"')
- {
- // strip leading and trailing '"'
- ssid++;
- ssid_len-=2;
- }
- if (ssid_len == 0) {
- ALOGE("Error setPnoListNative: zero length ssid, skip it");
- continue;
- }
- memcpy(params.networks[i].ssid, ssid, ssid_len);
-
- params.networks[i].auth_bit_field = helper.getByteField(pno_net, "auth_bit_field");
- params.networks[i].flags = helper.getByteField(pno_net, "flags");
- ALOGD(" setPnoListNative: idx %u auth %x flags %x [%s]", i,
- params.networks[i].auth_bit_field, params.networks[i].flags,
- params.networks[i].ssid);
- }
- params.min5GHz_rssi = helper.getIntField(settings, "min5GHzRssi");
- params.min24GHz_rssi = helper.getIntField(settings, "min24GHzRssi");
- params.initial_score_max = helper.getIntField(settings, "initialScoreMax");
- params.current_connection_bonus = helper.getIntField(settings, "currentConnectionBonus");
- params.same_network_bonus = helper.getIntField(settings, "sameNetworkBonus");
- params.secure_bonus = helper.getIntField(settings, "secureBonus");
- params.band5GHz_bonus = helper.getIntField(settings, "band5GHzBonus");
- params.num_networks = len;
-
- int result = hal_fn.wifi_set_epno_list(id, handle, ¶ms, handler);
- ALOGD(" setPnoListNative: result %d", result);
-
- return result >= 0;
-}
-
-static jboolean android_net_wifi_resetPnoListNative(
- JNIEnv *env, jclass cls, jint iface, jint id) {
-
- JNIHelper helper(env);
-
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("reset ePno list request [%d] = %p", id, handle);
-
- // stop pno
- int result = hal_fn.wifi_reset_epno_list(id, handle);
- ALOGD(" ressetPnoListNative: result = %d", result);
- return result >= 0;
-}
-
-static jboolean android_net_wifi_setBssidBlacklist(
- JNIEnv *env, jclass cls, jint iface, jint id, jobject list) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("configure BSSID black list request [%d] = %p", id, handle);
-
- wifi_bssid_params params;
- memset(¶ms, 0, sizeof(params));
-
- if (list != NULL) {
- size_t len = helper.getArrayLength((jobjectArray)list);
- if (len > (size_t)MAX_BLACKLIST_BSSID) {
- return false;
- }
- for (unsigned int i = 0; i < len; i++) {
-
- JNIObject<jobject> jbssid = helper.getObjectArrayElement(list, i);
- if (jbssid == NULL) {
- ALOGE("configure BSSID blacklist: could not get element %d", i);
- continue;
- }
-
- ScopedUtfChars chars(env, (jstring)jbssid.get());
- const char *bssid = chars.c_str();
- if (bssid == NULL) {
- ALOGE("Error getting bssid");
- return false;
- }
-
- mac_addr addr;
- parseMacAddress(bssid, addr);
- memcpy(params.bssids[i], addr, sizeof(mac_addr));
-
- char bssidOut[32];
- sprintf(bssidOut, "%0x:%0x:%0x:%0x:%0x:%0x", addr[0], addr[1],
- addr[2], addr[3], addr[4], addr[5]);
-
- ALOGD("BSSID blacklist: added bssid %s", bssidOut);
-
- params.num_bssid++;
- }
- }
-
- ALOGD("Added %d bssids", params.num_bssid);
- return hal_fn.wifi_set_bssid_blacklist(id, handle, params) == WIFI_SUCCESS;
-}
-
-static jint android_net_wifi_start_sending_offloaded_packet(JNIEnv *env, jclass cls, jint iface,
- jint idx, jbyteArray srcMac, jbyteArray dstMac, jbyteArray pkt, jint period) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("Start packet offload [%d] = %p", idx, handle);
- wifi_error ret;
- wifi_request_id id = idx;
-
- ScopedBytesRO pktBytes(env, pkt), srcMacBytes(env, srcMac), dstMacBytes(env, dstMac);
-
- byte * pkt_data = (byte*) pktBytes.get();
- unsigned short pkt_len = env->GetArrayLength(pkt);
- byte* src_mac_addr = (byte*) srcMacBytes.get();
- byte* dst_mac_addr = (byte*) dstMacBytes.get();
- int i;
- char macAddr[32];
- sprintf(macAddr, "%0x:%0x:%0x:%0x:%0x:%0x", src_mac_addr[0], src_mac_addr[1],
- src_mac_addr[2], src_mac_addr[3], src_mac_addr[4], src_mac_addr[5]);
- ALOGD("src_mac_addr %s", macAddr);
- sprintf(macAddr, "%0x:%0x:%0x:%0x:%0x:%0x", dst_mac_addr[0], dst_mac_addr[1],
- dst_mac_addr[2], dst_mac_addr[3], dst_mac_addr[4], dst_mac_addr[5]);
- ALOGD("dst_mac_addr %s", macAddr);
- ALOGD("pkt_len %d\n", pkt_len);
- ALOGD("Pkt data : ");
- for(i = 0; i < pkt_len; i++) {
- ALOGD(" %x ", pkt_data[i]);
- }
- ALOGD("\n");
- ret = hal_fn.wifi_start_sending_offloaded_packet(id, handle, pkt_data, pkt_len,
- src_mac_addr, dst_mac_addr, period);
- ALOGD("ret= %d\n", ret);
- return ret;
-}
-
-static jint android_net_wifi_stop_sending_offloaded_packet(JNIEnv *env, jclass cls,
- jint iface, jint idx) {
- int ret;
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("Stop packet offload [%d] = %p", idx, handle);
- ret = hal_fn.wifi_stop_sending_offloaded_packet(idx, handle);
- ALOGD("ret= %d\n", ret);
- return ret;
-}
-
-static void onRssiThresholdbreached(wifi_request_id id, u8 *cur_bssid, s8 cur_rssi) {
-
- ALOGD("RSSI threshold breached, cur RSSI - %d!!\n", cur_rssi);
- ALOGD("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n",
- cur_bssid[0], cur_bssid[1], cur_bssid[2],
- cur_bssid[3], cur_bssid[4], cur_bssid[5]);
- JNIHelper helper(mVM);
- //ALOGD("onRssiThresholdbreached called, vm = %p, obj = %p, env = %p", mVM, mCls, env);
- helper.reportEvent(mCls, "onRssiThresholdBreached", "(IB)V", id, cur_rssi);
-}
-
-static jint android_net_wifi_start_rssi_monitoring_native(JNIEnv *env, jclass cls, jint iface,
- jint idx, jbyte maxRssi, jbyte minRssi) {
-
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("Start Rssi monitoring = %p", handle);
- ALOGD("MinRssi %d MaxRssi %d", minRssi, maxRssi);
- wifi_error ret;
- wifi_request_id id = idx;
- wifi_rssi_event_handler eh;
- eh.on_rssi_threshold_breached = onRssiThresholdbreached;
- ret = hal_fn.wifi_start_rssi_monitoring(id, handle, maxRssi, minRssi, eh);
- return ret;
-}
-
-static jint android_net_wifi_stop_rssi_monitoring_native(JNIEnv *env, jclass cls,
- jint iface, jint idx) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- ALOGD("Stop Rssi monitoring = %p", handle);
- wifi_error ret;
- wifi_request_id id = idx;
- ret = hal_fn.wifi_stop_rssi_monitoring(id, handle);
- return ret;
-}
-
-static jobject android_net_wifi_get_wlan_wake_reason_count(JNIEnv *env, jclass cls, jint iface) {
-
- JNIHelper helper(env);
- WLAN_DRIVER_WAKE_REASON_CNT wake_reason_cnt;
- int cmd_event_wake_cnt_array[WAKE_REASON_TYPE_MAX];
- int driver_fw_local_wake_cnt_array[WAKE_REASON_TYPE_MAX];
- wifi_interface_handle handle = getIfaceHandle(helper, cls, iface);
- wifi_error ret;
-
- wake_reason_cnt.cmd_event_wake_cnt = cmd_event_wake_cnt_array;
- wake_reason_cnt.cmd_event_wake_cnt_sz = WAKE_REASON_TYPE_MAX;
- wake_reason_cnt.cmd_event_wake_cnt_used = 0;
-
- wake_reason_cnt.driver_fw_local_wake_cnt = driver_fw_local_wake_cnt_array;
- wake_reason_cnt.driver_fw_local_wake_cnt_sz = WAKE_REASON_TYPE_MAX;
- wake_reason_cnt.driver_fw_local_wake_cnt_used = 0;
-
- ret = hal_fn.wifi_get_wake_reason_stats(handle, &wake_reason_cnt);
-
- if (ret != WIFI_SUCCESS) {
- ALOGE("android_net_wifi_get_wlan_wake_reason_count: failed to get wake reason count\n");
- return NULL;
- }
-
- JNIObject<jobject> stats = helper.createObject( "android/net/wifi/WifiWakeReasonAndCounts");
- if (stats == NULL) {
- ALOGE("android_net_wifi_get_wlan_wake_reason_count: error allocating object\n");
- return NULL;
- }
- JNIObject<jintArray> cmd_wake_arr =
- helper.newIntArray(wake_reason_cnt.cmd_event_wake_cnt_used);
- if (cmd_wake_arr == NULL) {
- ALOGE("android_net_wifi_get_wlan_wake_reason_count: error allocating array object\n");
- return NULL;
- }
- JNIObject<jintArray> local_wake_arr =
- helper.newIntArray(wake_reason_cnt.driver_fw_local_wake_cnt_used);
- if (local_wake_arr == NULL) {
- ALOGE("android_net_wifi_get_wlan_wake_reason_count: error allocating array object\n");
- return NULL;
- }
-
- helper.setIntField(stats, "totalCmdEventWake", wake_reason_cnt.total_cmd_event_wake);
- helper.setIntField(stats, "totalDriverFwLocalWake", wake_reason_cnt.total_driver_fw_local_wake);
- helper.setIntField(stats, "totalRxDataWake", wake_reason_cnt.total_rx_data_wake);
- helper.setIntField(stats, "rxUnicast", wake_reason_cnt.rx_wake_details.rx_unicast_cnt);
- helper.setIntField(stats, "rxMulticast", wake_reason_cnt.rx_wake_details.rx_multicast_cnt);
- helper.setIntField(stats, "rxBroadcast", wake_reason_cnt.rx_wake_details.rx_broadcast_cnt);
- helper.setIntField(stats, "icmp", wake_reason_cnt.rx_wake_pkt_classification_info.icmp_pkt);
- helper.setIntField(stats, "icmp6", wake_reason_cnt.rx_wake_pkt_classification_info.icmp6_pkt);
- helper.setIntField(stats, "icmp6Ra", wake_reason_cnt.rx_wake_pkt_classification_info.icmp6_ra);
- helper.setIntField(stats, "icmp6Na", wake_reason_cnt.rx_wake_pkt_classification_info.icmp6_na);
- helper.setIntField(stats, "icmp6Ns", wake_reason_cnt.rx_wake_pkt_classification_info.icmp6_ns);
- helper.setIntField(stats, "ipv4RxMulticast",
- wake_reason_cnt.rx_multicast_wake_pkt_info.ipv4_rx_multicast_addr_cnt);
- helper.setIntField(stats, "ipv6Multicast",
- wake_reason_cnt.rx_multicast_wake_pkt_info.ipv6_rx_multicast_addr_cnt);
- helper.setIntField(stats, "otherRxMulticast",
- wake_reason_cnt.rx_multicast_wake_pkt_info.other_rx_multicast_addr_cnt);
- helper.setIntArrayRegion(cmd_wake_arr, 0, wake_reason_cnt.cmd_event_wake_cnt_used,
- wake_reason_cnt.cmd_event_wake_cnt);
- helper.setIntArrayRegion(local_wake_arr, 0, wake_reason_cnt.driver_fw_local_wake_cnt_used,
- wake_reason_cnt.driver_fw_local_wake_cnt);
- helper.setObjectField(stats, "cmdEventWakeCntArray", "[I", cmd_wake_arr);
- helper.setObjectField(stats, "driverFWLocalWakeCntArray", "[I", local_wake_arr);
- return stats.detach();
-}
static jbyteArray android_net_wifi_readKernelLog(JNIEnv *env, jclass cls) {
JNIHelper helper(env);
@@ -2510,15 +72,6 @@
return result.detach();
}
-static jint android_net_wifi_configure_nd_offload(JNIEnv *env, jclass cls,
- jint iface, jboolean enable) {
- JNIHelper helper(env);
- return hal_fn.wifi_configure_nd_offload(
- getIfaceHandle(helper, cls, iface),
- static_cast<int>(enable));
-}
-
-
// ----------------------------------------------------------------------------
/*
@@ -2526,112 +79,7 @@
*/
static JNINativeMethod gWifiMethods[] = {
/* name, signature, funcPtr */
-
- { "loadDriverNative", "()Z", (void *)android_net_wifi_loadDriver },
- { "isDriverLoadedNative", "()Z", (void *)android_net_wifi_isDriverLoaded },
- { "unloadDriverNative", "()Z", (void *)android_net_wifi_unloadDriver },
- { "startSupplicantNative", "(Z)Z", (void *)android_net_wifi_startSupplicant },
- { "killSupplicantNative", "(Z)Z", (void *)android_net_wifi_killSupplicant },
- { "connectToSupplicantNative", "()Z", (void *)android_net_wifi_connectToSupplicant },
- { "closeSupplicantConnectionNative", "()V",
- (void *)android_net_wifi_closeSupplicantConnection },
- { "waitForEventNative", "()Ljava/lang/String;", (void*)android_net_wifi_waitForEvent },
- { "doBooleanCommandNative", "(Ljava/lang/String;)Z", (void*)android_net_wifi_doBooleanCommand },
- { "doIntCommandNative", "(Ljava/lang/String;)I", (void*)android_net_wifi_doIntCommand },
- { "doStringCommandNative", "(Ljava/lang/String;)Ljava/lang/String;",
- (void*) android_net_wifi_doStringCommand },
- { "startHalNative", "()Z", (void*) android_net_wifi_startHal },
- { "stopHalNative", "()V", (void*) android_net_wifi_stopHal },
- { "waitForHalEventNative", "()V", (void*) android_net_wifi_waitForHalEvents },
- { "getInterfacesNative", "()I", (void*) android_net_wifi_getInterfaces},
- { "getInterfaceNameNative", "(I)Ljava/lang/String;", (void*) android_net_wifi_getInterfaceName},
- { "getScanCapabilitiesNative", "(ILcom/android/server/wifi/WifiNative$ScanCapabilities;)Z",
- (void *) android_net_wifi_getScanCapabilities},
- { "startScanNative", "(IILcom/android/server/wifi/WifiNative$ScanSettings;)Z",
- (void*) android_net_wifi_startScan},
- { "stopScanNative", "(II)Z", (void*) android_net_wifi_stopScan},
- { "getScanResultsNative", "(IZ)[Landroid/net/wifi/WifiScanner$ScanData;",
- (void *) android_net_wifi_getScanResults},
- { "setHotlistNative", "(IILandroid/net/wifi/WifiScanner$HotlistSettings;)Z",
- (void*) android_net_wifi_setHotlist},
- { "resetHotlistNative", "(II)Z", (void*) android_net_wifi_resetHotlist},
- { "trackSignificantWifiChangeNative", "(IILandroid/net/wifi/WifiScanner$WifiChangeSettings;)Z",
- (void*) android_net_wifi_trackSignificantWifiChange},
- { "untrackSignificantWifiChangeNative", "(II)Z",
- (void*) android_net_wifi_untrackSignificantWifiChange},
- { "getWifiLinkLayerStatsNative", "(I)Landroid/net/wifi/WifiLinkLayerStats;",
- (void*) android_net_wifi_getLinkLayerStats},
- { "setWifiLinkLayerStatsNative", "(II)V",
- (void*) android_net_wifi_setLinkLayerStats},
- { "getSupportedFeatureSetNative", "(I)I",
- (void*) android_net_wifi_getSupportedFeatures},
- { "requestRangeNative", "(II[Landroid/net/wifi/RttManager$RttParams;)Z",
- (void*) android_net_wifi_requestRange},
- { "cancelRangeRequestNative", "(II[Landroid/net/wifi/RttManager$RttParams;)Z",
- (void*) android_net_wifi_cancelRange},
- { "enableRttResponderNative",
- "(IIILcom/android/server/wifi/WifiNative$WifiChannelInfo;)Landroid/net/wifi/RttManager$ResponderConfig;",
- (void*) android_net_wifi_enableResponder},
- { "disableRttResponderNative", "(II)Z",
- (void*) android_net_wifi_disableResponder},
-
- { "setScanningMacOuiNative", "(I[B)Z", (void*) android_net_wifi_setScanningMacOui},
- { "getChannelsForBandNative", "(II)[I", (void*) android_net_wifi_getValidChannels},
- { "setDfsFlagNative", "(IZ)Z", (void*) android_net_wifi_setDfsFlag},
- { "setInterfaceUpNative", "(Z)Z", (void*) android_net_wifi_set_interface_up},
- { "getRttCapabilitiesNative", "(I)Landroid/net/wifi/RttManager$RttCapabilities;",
- (void*) android_net_wifi_get_rtt_capabilities},
- { "getApfCapabilitiesNative", "(I)Landroid/net/apf/ApfCapabilities;",
- (void*) android_net_wifi_get_apf_capabilities},
- { "installPacketFilterNative", "(I[B)Z", (void*) android_net_wifi_install_packet_filter},
- {"setCountryCodeHalNative", "(ILjava/lang/String;)Z",
- (void*) android_net_wifi_set_Country_Code_Hal},
- { "setPnoListNative", "(IILcom/android/server/wifi/WifiNative$PnoSettings;)Z",
- (void*) android_net_wifi_setPnoListNative},
- { "resetPnoListNative", "(II)Z", (void*) android_net_wifi_resetPnoListNative},
- {"enableDisableTdlsNative", "(IZLjava/lang/String;)Z",
- (void*) android_net_wifi_enable_disable_tdls},
- {"getTdlsStatusNative", "(ILjava/lang/String;)Lcom/android/server/wifi/WifiNative$TdlsStatus;",
- (void*) android_net_wifi_get_tdls_status},
- {"getTdlsCapabilitiesNative", "(I)Lcom/android/server/wifi/WifiNative$TdlsCapabilities;",
- (void*) android_net_wifi_get_tdls_capabilities},
- {"getSupportedLoggerFeatureSetNative","(I)I",
- (void*) android_net_wifi_get_supported_logger_feature},
- {"getDriverVersionNative", "(I)Ljava/lang/String;",
- (void*) android_net_wifi_get_driver_version},
- {"getFirmwareVersionNative", "(I)Ljava/lang/String;",
- (void*) android_net_wifi_get_firmware_version},
- {"getRingBufferStatusNative", "(I)[Lcom/android/server/wifi/WifiNative$RingBufferStatus;",
- (void*) android_net_wifi_get_ring_buffer_status},
- {"startLoggingRingBufferNative", "(IIIIILjava/lang/String;)Z",
- (void*) android_net_wifi_start_logging_ring_buffer},
- {"getRingBufferDataNative", "(ILjava/lang/String;)Z",
- (void*) android_net_wifi_get_ring_buffer_data},
- {"getFwMemoryDumpNative","(I)Z", (void*) android_net_wifi_get_fw_memory_dump},
- {"getDriverStateDumpNative","(I)[B", (void*) android_net_wifi_get_driver_state_dump},
- { "setBssidBlacklistNative", "(II[Ljava/lang/String;)Z",
- (void*)android_net_wifi_setBssidBlacklist},
- {"setLoggingEventHandlerNative", "(II)Z", (void *) android_net_wifi_set_log_handler},
- {"resetLogHandlerNative", "(II)Z", (void *) android_net_wifi_reset_log_handler},
- {"startPktFateMonitoringNative", "(I)I", (void*) android_net_wifi_start_pkt_fate_monitoring},
- {"getTxPktFatesNative", "(I[Lcom/android/server/wifi/WifiNative$TxFateReport;)I",
- (void*) android_net_wifi_get_tx_pkt_fates},
- {"getRxPktFatesNative", "(I[Lcom/android/server/wifi/WifiNative$RxFateReport;)I",
- (void*) android_net_wifi_get_rx_pkt_fates},
- { "startSendingOffloadedPacketNative", "(II[B[B[BI)I",
- (void*)android_net_wifi_start_sending_offloaded_packet},
- { "stopSendingOffloadedPacketNative", "(II)I",
- (void*)android_net_wifi_stop_sending_offloaded_packet},
- {"startRssiMonitoringNative", "(IIBB)I",
- (void*)android_net_wifi_start_rssi_monitoring_native},
- {"stopRssiMonitoringNative", "(II)I",
- (void*)android_net_wifi_stop_rssi_monitoring_native},
- { "getWlanWakeReasonCountNative", "(I)Landroid/net/wifi/WifiWakeReasonAndCounts;",
- (void*) android_net_wifi_get_wlan_wake_reason_count},
- {"isGetChannelsForBandSupportedNative", "()Z",
- (void*)android_net_wifi_is_get_channels_for_band_supported},
{"readKernelLogNative", "()[B", (void*)android_net_wifi_readKernelLog},
- {"configureNeighborDiscoveryOffload", "(IZ)I", (void*)android_net_wifi_configure_nd_offload},
};
/* User to register native functions */
diff --git a/service/jni/com_android_server_wifi_nan_WifiNanNative.cpp b/service/jni/com_android_server_wifi_nan_WifiNanNative.cpp
deleted file mode 100644
index cae441a..0000000
--- a/service/jni/com_android_server_wifi_nan_WifiNanNative.cpp
+++ /dev/null
@@ -1,523 +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.
- */
-
-#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 "jni_helper.h"
-
-namespace android {
-
-static jclass mCls; /* saved WifiNanNative object */
-static JavaVM *mVM = NULL; /* saved JVM pointer */
-
-wifi_handle getWifiHandle(JNIHelper &helper, jclass cls);
-wifi_interface_handle getIfaceHandle(JNIHelper &helper, jclass cls, jint index);
-
-extern wifi_hal_fn hal_fn;
-
-// Start NAN functions
-
-static void OnNanNotifyResponse(transaction_id id, NanResponseMsg* msg) {
- ALOGD(
- "OnNanNotifyResponse: transaction_id=%d, status=%d, value=%d, response_type=%d",
- id, msg->status, msg->value, msg->response_type);
-
- JNIHelper helper(mVM);
- switch (msg->response_type) {
- case NAN_RESPONSE_PUBLISH:
- helper.reportEvent(mCls, "onNanNotifyResponsePublishSubscribe",
- "(SIIII)V", (short) id, (int) msg->response_type,
- (int) msg->status, (int) msg->value,
- msg->body.publish_response.publish_id);
- break;
- case NAN_RESPONSE_SUBSCRIBE:
- helper.reportEvent(mCls, "onNanNotifyResponsePublishSubscribe",
- "(SIIII)V", (short) id, (int) msg->response_type,
- (int) msg->status, (int) msg->value,
- msg->body.subscribe_response.subscribe_id);
- break;
- case NAN_GET_CAPABILITIES: {
- JNIObject<jobject> data = helper.createObject(
- "com/android/server/wifi/nan/WifiNanNative$Capabilities");
- if (data == NULL) {
- ALOGE(
- "Error in allocating WifiNanNative.Capabilities OnNanNotifyResponse");
- return;
- }
-
- helper.setIntField(
- data, "maxConcurrentNanClusters",
- (int) msg->body.nan_capabilities.max_concurrent_nan_clusters);
- helper.setIntField(data, "maxPublishes",
- (int) msg->body.nan_capabilities.max_publishes);
- helper.setIntField(data, "maxSubscribes",
- (int) msg->body.nan_capabilities.max_subscribes);
- helper.setIntField(data, "maxServiceNameLen",
- (int) msg->body.nan_capabilities.max_service_name_len);
- helper.setIntField(data, "maxMatchFilterLen",
- (int) msg->body.nan_capabilities.max_match_filter_len);
- helper.setIntField(
- data, "maxTotalMatchFilterLen",
- (int) msg->body.nan_capabilities.max_total_match_filter_len);
- helper.setIntField(
- data, "maxServiceSpecificInfoLen",
- (int) msg->body.nan_capabilities.max_service_specific_info_len);
- helper.setIntField(data, "maxVsaDataLen",
- (int) msg->body.nan_capabilities.max_vsa_data_len);
- helper.setIntField(data, "maxMeshDataLen",
- (int) msg->body.nan_capabilities.max_mesh_data_len);
- helper.setIntField(data, "maxNdiInterfaces",
- (int) msg->body.nan_capabilities.max_ndi_interfaces);
- helper.setIntField(data, "maxNdpSessions",
- (int) msg->body.nan_capabilities.max_ndp_sessions);
- helper.setIntField(data, "maxAppInfoLen",
- (int) msg->body.nan_capabilities.max_app_info_len);
-
- helper.reportEvent(
- mCls, "onNanNotifyResponseCapabilities",
- "(SIILcom/android/server/wifi/nan/WifiNanNative$Capabilities;)V",
- (short) id, (int) msg->status, (int) msg->value, data.get());
- break;
- }
- default:
- helper.reportEvent(mCls, "onNanNotifyResponse", "(SIII)V", (short) id,
- (int) msg->response_type, (int) msg->status,
- (int) msg->value);
- break;
- }
-}
-
-static void OnNanEventPublishTerminated(NanPublishTerminatedInd* event) {
- ALOGD("OnNanEventPublishTerminated");
-
- JNIHelper helper(mVM);
- helper.reportEvent(mCls, "onPublishTerminated", "(II)V",
- event->publish_id, event->reason);
-}
-
-static void OnNanEventMatch(NanMatchInd* event) {
- ALOGD("OnNanEventMatch");
-
- JNIHelper helper(mVM);
-
- JNIObject<jbyteArray> macBytes = helper.newByteArray(6);
- helper.setByteArrayRegion(macBytes, 0, 6, (jbyte *) event->addr);
-
- JNIObject<jbyteArray> ssiBytes = helper.newByteArray(event->service_specific_info_len);
- helper.setByteArrayRegion(ssiBytes, 0, event->service_specific_info_len,
- (jbyte *) event->service_specific_info);
-
- JNIObject<jbyteArray> mfBytes = helper.newByteArray(event->sdf_match_filter_len);
- helper.setByteArrayRegion(mfBytes, 0, event->sdf_match_filter_len,
- (jbyte *) event->sdf_match_filter);
-
- helper.reportEvent(mCls, "onMatchEvent", "(II[B[BI[BI)V",
- (int) event->publish_subscribe_id,
- (int) event->requestor_instance_id,
- macBytes.get(),
- ssiBytes.get(), event->service_specific_info_len,
- mfBytes.get(), event->sdf_match_filter_len);
-}
-
-static void OnNanEventMatchExpired(NanMatchExpiredInd* event) {
- ALOGD("OnNanEventMatchExpired");
-}
-
-static void OnNanEventSubscribeTerminated(NanSubscribeTerminatedInd* event) {
- ALOGD("OnNanEventSubscribeTerminated");
-
- JNIHelper helper(mVM);
- helper.reportEvent(mCls, "onSubscribeTerminated", "(II)V",
- event->subscribe_id, event->reason);
-}
-
-static void OnNanEventFollowup(NanFollowupInd* event) {
- ALOGD("OnNanEventFollowup");
-
- JNIHelper helper(mVM);
-
- JNIObject<jbyteArray> macBytes = helper.newByteArray(6);
- helper.setByteArrayRegion(macBytes, 0, 6, (jbyte *) event->addr);
-
- JNIObject<jbyteArray> msgBytes = helper.newByteArray(event->service_specific_info_len);
- helper.setByteArrayRegion(msgBytes, 0, event->service_specific_info_len, (jbyte *) event->service_specific_info);
-
- helper.reportEvent(mCls, "onFollowupEvent", "(II[B[BI)V",
- (int) event->publish_subscribe_id,
- (int) event->requestor_instance_id,
- macBytes.get(),
- msgBytes.get(),
- (int) event->service_specific_info_len);
-}
-
-static void OnNanEventDiscEngEvent(NanDiscEngEventInd* event) {
- ALOGD("OnNanEventDiscEngEvent called: event_type=%d", event->event_type);
-
- JNIHelper helper(mVM);
-
- JNIObject<jbyteArray> macBytes = helper.newByteArray(6);
- if (event->event_type == NAN_EVENT_ID_DISC_MAC_ADDR) {
- helper.setByteArrayRegion(macBytes, 0, 6, (jbyte *) event->data.mac_addr.addr);
- } else {
- helper.setByteArrayRegion(macBytes, 0, 6, (jbyte *) event->data.cluster.addr);
- }
-
- helper.reportEvent(mCls, "onDiscoveryEngineEvent", "(I[B)V",
- (int) event->event_type, macBytes.get());
-}
-
-static void OnNanEventDisabled(NanDisabledInd* event) {
- ALOGD("OnNanEventDisabled called: reason=%d", event->reason);
-
- JNIHelper helper(mVM);
-
- helper.reportEvent(mCls, "onDisabledEvent", "(I)V", (int) event->reason);
-}
-
-static void OnNanEventTca(NanTCAInd* event) {
- ALOGD("OnNanEventTca");
-}
-
-static void OnNanEventBeaconSdfPayload(NanBeaconSdfPayloadInd* event) {
- ALOGD("OnNanEventSdfPayload");
-}
-
-static jint android_net_wifi_nan_register_handler(JNIEnv *env, jclass cls,
- jclass wifi_native_cls,
- jint iface) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
-
- ALOGD("android_net_wifi_nan_register_handler handle=%p", handle);
-
- NanCallbackHandler handlers;
- handlers.NotifyResponse = OnNanNotifyResponse;
- handlers.EventPublishTerminated = OnNanEventPublishTerminated;
- handlers.EventMatch = OnNanEventMatch;
- handlers.EventMatchExpired = OnNanEventMatchExpired;
- handlers.EventSubscribeTerminated = OnNanEventSubscribeTerminated;
- handlers.EventFollowup = OnNanEventFollowup;
- handlers.EventDiscEngEvent = OnNanEventDiscEngEvent;
- handlers.EventDisabled = OnNanEventDisabled;
- handlers.EventTca = OnNanEventTca;
- handlers.EventBeaconSdfPayload = OnNanEventBeaconSdfPayload;
-
- if (mVM == NULL) {
- env->GetJavaVM(&mVM);
- mCls = (jclass) env->NewGlobalRef(cls);
- }
-
- return hal_fn.wifi_nan_register_handler(handle, handlers);
-}
-
-static jint android_net_wifi_nan_enable_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_enable_request handle=%p, id=%d",
- handle, transaction_id);
-
- NanEnableRequest msg;
- memset(&msg, 0, sizeof(NanEnableRequest));
-
- /* configurable settings */
- msg.config_support_5g = 1;
- msg.support_5g_val = helper.getBoolField(config_request, "mSupport5gBand");
- msg.master_pref = helper.getIntField(config_request, "mMasterPreference");
- msg.cluster_low = helper.getIntField(config_request, "mClusterLow");
- msg.cluster_high = helper.getIntField(config_request, "mClusterHigh");
-
- return hal_fn.wifi_nan_enable_request(transaction_id, handle, &msg);
-}
-
-static jint android_net_wifi_nan_get_capabilities(JNIEnv *env, jclass cls,
- jshort transaction_id,
- jclass wifi_native_cls,
- jint iface) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
-
- ALOGD("android_net_wifi_nan_get_capabilities handle=%p, id=%d", handle,
- transaction_id);
-
- return hal_fn.wifi_nan_get_capabilities(transaction_id, handle);
-}
-
-static jint android_net_wifi_nan_disable_request(JNIEnv *env, jclass cls,
- jshort transaction_id,
- jclass wifi_native_cls,
- jint iface) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
-
- ALOGD("android_net_wifi_nan_disable_request handle=%p, id=%d",
- handle, transaction_id);
-
- return hal_fn.wifi_nan_disable_request(transaction_id, handle);
-}
-
-static jint android_net_wifi_nan_publish(JNIEnv *env, jclass cls,
- jshort transaction_id,
- jint publish_id,
- jclass wifi_native_cls,
- jint iface,
- jobject publish_data,
- jobject publish_settings) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
-
- ALOGD("android_net_wifi_nan_publish handle=%p, id=%d", handle, transaction_id);
-
- NanPublishRequest msg;
- memset(&msg, 0, sizeof(NanPublishRequest));
-
- /* hard-coded settings - TBD: move to configurable */
- msg.period = 500;
- msg.publish_match_indicator = NAN_MATCH_ALG_MATCH_ONCE;
- msg.rssi_threshold_flag = 0;
- msg.connmap = 0;
-
- /* configurable settings */
- msg.publish_id = publish_id;
-
- JNIObject<jstring> objStr1 = helper.getStringField(publish_data, "mServiceName");
- if (objStr1 == NULL) {
- ALOGE("Error accessing mServiceName field");
- 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_specific_info_len = helper.getIntField(publish_data, "mServiceSpecificInfoLength");
- if (msg.service_specific_info_len != 0) {
- helper.getByteArrayField(publish_data, "mServiceSpecificInfo",
- msg.service_specific_info, msg.service_specific_info_len);
- }
-
-
- msg.tx_match_filter_len = helper.getIntField(publish_data, "mTxFilterLength");
- if (msg.tx_match_filter_len != 0) {
- helper.getByteArrayField(publish_data, "mTxFilter",
- msg.tx_match_filter, msg.tx_match_filter_len);
- }
-
- msg.rx_match_filter_len = helper.getIntField(publish_data, "mRxFilterLength");
- if (msg.rx_match_filter_len != 0) {
- helper.getByteArrayField(publish_data, "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.tx_type = NAN_TX_TYPE_BROADCAST;
- if (msg.publish_type != NAN_PUBLISH_TYPE_UNSOLICITED)
- msg.tx_type = NAN_TX_TYPE_UNICAST;
-
- return hal_fn.wifi_nan_publish_request(transaction_id, handle, &msg);
-}
-
-static jint android_net_wifi_nan_subscribe(JNIEnv *env, jclass cls,
- jshort transaction_id,
- jint subscribe_id,
- jclass wifi_native_cls,
- jint iface,
- jobject subscribe_data,
- jobject subscribe_settings) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
-
- ALOGD("android_net_wifi_nan_subscribe handle=%p, id=%d", handle, transaction_id);
-
- NanSubscribeRequest msg;
- memset(&msg, 0, sizeof(NanSubscribeRequest));
-
- /* hard-coded settings - TBD: move to configurable */
- msg.period = 500;
- msg.serviceResponseFilter = NAN_SRF_ATTR_PARTIAL_MAC_ADDR;
- 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;
-
- /* configurable settings */
- msg.subscribe_id = subscribe_id;
-
- JNIObject<jstring> objStr1 = helper.getStringField(subscribe_data, "mServiceName");
- if (objStr1 == NULL) {
- ALOGE("Error accessing mServiceName field");
- 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_specific_info_len = helper.getIntField(subscribe_data, "mServiceSpecificInfoLength");
- if (msg.service_specific_info_len != 0) {
- helper.getByteArrayField(subscribe_data, "mServiceSpecificInfo",
- msg.service_specific_info, msg.service_specific_info_len);
- }
-
- msg.tx_match_filter_len = helper.getIntField(subscribe_data, "mTxFilterLength");
- if (msg.tx_match_filter_len != 0) {
- helper.getByteArrayField(subscribe_data, "mTxFilter",
- msg.tx_match_filter, msg.tx_match_filter_len);
- }
-
- msg.rx_match_filter_len = helper.getIntField(subscribe_data, "mRxFilterLength");
- if (msg.rx_match_filter_len != 0) {
- helper.getByteArrayField(subscribe_data, "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");
-
- return hal_fn.wifi_nan_subscribe_request(transaction_id, handle, &msg);
-}
-
-static jint android_net_wifi_nan_send_message(JNIEnv *env, jclass cls,
- jshort transaction_id,
- jclass wifi_native_cls,
- jint iface,
- jint pub_sub_id,
- jint req_instance_id,
- jbyteArray dest,
- jbyteArray message,
- jint message_length) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
-
- ALOGD("android_net_wifi_nan_send_message handle=%p, id=%d", handle, transaction_id);
-
- NanTransmitFollowupRequest msg;
- memset(&msg, 0, sizeof(NanTransmitFollowupRequest));
-
- /* hard-coded settings - TBD: move to configurable */
- msg.publish_subscribe_id = pub_sub_id;
- msg.requestor_instance_id = req_instance_id;
- msg.priority = NAN_TX_PRIORITY_NORMAL;
- msg.dw_or_faw = NAN_TRANSMIT_IN_DW;
-
- /* configurable settings */
- msg.service_specific_info_len = message_length;
-
- ScopedBytesRO messageBytes(env, message);
- memcpy(msg.service_specific_info, (byte*) messageBytes.get(), message_length);
-
- ScopedBytesRO destBytes(env, dest);
- memcpy(msg.addr, (byte*) destBytes.get(), 6);
-
- return hal_fn.wifi_nan_transmit_followup_request(transaction_id, handle, &msg);
-}
-
-static jint android_net_wifi_nan_stop_publish(JNIEnv *env, jclass cls,
- jshort transaction_id,
- jclass wifi_native_cls,
- jint iface,
- jint pub_sub_id) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
-
- ALOGD("android_net_wifi_nan_stop_publish handle=%p, id=%d", handle, transaction_id);
-
- NanPublishCancelRequest msg;
- memset(&msg, 0, sizeof(NanPublishCancelRequest));
-
- msg.publish_id = pub_sub_id;
-
- return hal_fn.wifi_nan_publish_cancel_request(transaction_id, handle, &msg);
-}
-
-static jint android_net_wifi_nan_stop_subscribe(JNIEnv *env, jclass cls,
- jshort transaction_id,
- jclass wifi_native_cls,
- jint iface,
- jint pub_sub_id) {
- JNIHelper helper(env);
- wifi_interface_handle handle = getIfaceHandle(helper, wifi_native_cls, iface);
-
- ALOGD("android_net_wifi_nan_stop_subscribe handle=%p, id=%d", handle, transaction_id);
-
- NanSubscribeCancelRequest msg;
- memset(&msg, 0, sizeof(NanSubscribeCancelRequest));
-
- msg.subscribe_id = pub_sub_id;
-
- return hal_fn.wifi_nan_subscribe_cancel_request(transaction_id, handle, &msg);
-}
-
-// ----------------------------------------------------------------------------
-
-/*
- * JNI registration.
- */
-
-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 },
-};
-
-/* User to register native functions */
-extern "C"
-jint Java_com_android_server_wifi_nan_WifiNanNative_registerNanNatives(JNIEnv* env, jclass clazz) {
- return jniRegisterNativeMethods(env,
- "com/android/server/wifi/nan/WifiNanNative", gWifiNanMethods, NELEM(gWifiNanMethods));
-}
-
-}; // namespace android
diff --git a/service/jni/jni_helper.cpp b/service/jni/jni_helper.cpp
index c9b4edd..150f694 100644
--- a/service/jni/jni_helper.cpp
+++ b/service/jni/jni_helper.cpp
@@ -16,15 +16,13 @@
#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.h"
-#include "wifi_hal.h"
#include "jni_helper.h"
namespace android {
@@ -52,628 +50,19 @@
}
}
-jobject JNIHelper::newGlobalRef(jobject obj) {
- return mEnv->NewGlobalRef(obj);
-}
-
-void JNIHelper::deleteGlobalRef(jobject obj) {
- mEnv->DeleteGlobalRef(obj);
-}
-
jobject JNIHelper::newLocalRef(jobject obj) {
- return mEnv->NewLocalRef(obj);
+ return mEnv->NewLocalRef(obj);
}
void JNIHelper::deleteLocalRef(jobject obj) {
- mEnv->DeleteLocalRef(obj);
-}
-
-void JNIHelper::throwException(const char *message, int line)
-{
- ALOGE("error at line %d: %s", line, message);
-
- const char *className = "java/lang/Exception";
-
- jclass exClass = mEnv->FindClass(className );
- if ( exClass == NULL ) {
- ALOGE("Could not find exception class to throw error");
- ALOGE("error at line %d: %s", line, message);
- return;
- }
-
- mEnv->ThrowNew(exClass, message);
-}
-
-jboolean JNIHelper::getBoolField(jobject obj, const char *name)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- jfieldID field = mEnv->GetFieldID(cls, name, "Z");
- if (field == 0) {
- THROW(*this, "Error in accessing field");
- return 0;
- }
-
- return mEnv->GetBooleanField(obj, field);
-}
-
-jint JNIHelper::getIntField(jobject obj, const char *name)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- jfieldID field = mEnv->GetFieldID(cls, name, "I");
- if (field == 0) {
- THROW(*this, "Error in accessing field");
- return 0;
- }
-
- return mEnv->GetIntField(obj, field);
-}
-
-jbyte JNIHelper::getByteField(jobject obj, const char *name)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- jfieldID field = mEnv->GetFieldID(cls, name, "B");
- if (field == 0) {
- THROW(*this, "Error in accessing field");
- return 0;
- }
-
- return mEnv->GetByteField(obj, field);
-}
-
-jlong JNIHelper::getLongField(jobject obj, const char *name)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- jfieldID field = mEnv->GetFieldID(cls, name, "J");
- if (field == 0) {
- THROW(*this, "Error in accessing field");
- return 0;
- }
-
- return mEnv->GetLongField(obj, field);
-}
-
-JNIObject<jstring> JNIHelper::getStringField(jobject obj, const char *name)
-{
- JNIObject<jobject> m = getObjectField(obj, name, "Ljava/lang/String;");
- if (m == NULL) {
- THROW(*this, "Error in accessing field");
- return JNIObject<jstring>(*this, NULL);
- }
-
- return JNIObject<jstring>(*this, (jstring)m.detach());
-}
-
-bool JNIHelper::getStringFieldValue(jobject obj, const char *name, char *buf, int size)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- jfieldID field = mEnv->GetFieldID(cls, name, "Ljava/lang/String;");
- if (field == 0) {
- THROW(*this, "Error in accessing field");
- return 0;
- }
-
- JNIObject<jobject> value(*this, mEnv->GetObjectField(obj, field));
- JNIObject<jstring> string(*this, (jstring)value.clone());
- ScopedUtfChars chars(mEnv, string);
-
- const char *utf = chars.c_str();
- if (utf == NULL) {
- THROW(*this, "Error in accessing value");
- return false;
- }
-
- if (*utf != 0 && size < 1) {
- return false;
- }
-
- strncpy(buf, utf, size);
- if (size > 0) {
- buf[size - 1] = 0;
- }
-
- return true;
-}
-
-jlong JNIHelper::getStaticLongField(jobject obj, const char *name)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- return getStaticLongField(cls, name);
-}
-
-jlong JNIHelper::getStaticLongField(jclass cls, const char *name)
-{
- jfieldID field = mEnv->GetStaticFieldID(cls, name, "J");
- if (field == 0) {
- THROW(*this, "Error in accessing field");
- return 0;
- }
- //ALOGE("getStaticLongField %s %p", name, cls);
- return mEnv->GetStaticLongField(cls, field);
-}
-
-JNIObject<jobject> JNIHelper::getObjectField(jobject obj, const char *name, const char *type)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- jfieldID field = mEnv->GetFieldID(cls, name, type);
- if (field == 0) {
- THROW(*this, "Error in accessing field");
- return JNIObject<jobject>(*this, NULL);
- }
-
- return JNIObject<jobject>(*this, mEnv->GetObjectField(obj, field));
-}
-
-JNIObject<jobjectArray> JNIHelper::getArrayField(jobject obj, const char *name, const char *type)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- jfieldID field = mEnv->GetFieldID(cls, name, type);
- if (field == 0) {
- THROW(*this, "Error in accessing field");
- return JNIObject<jobjectArray>(*this, NULL);
- }
-
- return JNIObject<jobjectArray>(*this, (jobjectArray)mEnv->GetObjectField(obj, field));
-}
-
-jlong JNIHelper::getLongArrayField(jobject obj, const char *name, int index)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- jfieldID field = mEnv->GetFieldID(cls, name, "[J");
- if (field == 0) {
- THROW(*this, "Error in accessing field definition");
- return 0;
- }
-
- JNIObject<jlongArray> array(*this, (jlongArray)mEnv->GetObjectField(obj, field));
- if (array == NULL) {
- THROW(*this, "Error in accessing array");
- return 0;
- }
-
- jlong *elem = mEnv->GetLongArrayElements(array, 0);
- if (elem == NULL) {
- THROW(*this, "Error in accessing index element");
- return 0;
- }
-
- jlong value = elem[index];
- mEnv->ReleaseLongArrayElements(array, elem, 0);
- return value;
-}
-
-void JNIHelper::getByteArrayField(jobject obj, const char *name, byte* buf, int 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;
- }
-
- JNIObject<jbyteArray> array(*this, (jbyteArray)mEnv->GetObjectField(obj, field));
- if (array == NULL) {
- THROW(*this, "Error in accessing array");
- return;
- }
-
- jbyte *elem = mEnv->GetByteArrayElements(array, 0);
- if (elem == NULL) {
- THROW(*this, "Error in accessing index element");
- return;
- }
-
- memcpy(buf, elem, size);
- mEnv->ReleaseByteArrayElements(array, elem, 0);
-}
-
-jlong JNIHelper::getStaticLongArrayField(jobject obj, const char *name, int index)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- return getStaticLongArrayField(cls, name, index);
-}
-
-jlong JNIHelper::getStaticLongArrayField(jclass cls, const char *name, int index)
-{
- jfieldID field = mEnv->GetStaticFieldID(cls, name, "[J");
- if (field == 0) {
- THROW(*this, "Error in accessing field definition");
- return 0;
- }
-
- JNIObject<jlongArray> array(*this, (jlongArray)mEnv->GetStaticObjectField(cls, field));
- jlong *elem = mEnv->GetLongArrayElements(array, 0);
- if (elem == NULL) {
- THROW(*this, "Error in accessing index element");
- return 0;
- }
-
- jlong value = elem[index];
- mEnv->ReleaseLongArrayElements(array, elem, 0);
- return value;
-}
-
-JNIObject<jobject> JNIHelper::getObjectArrayField(jobject obj, const char *name, const char *type,
-int index)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- jfieldID field = mEnv->GetFieldID(cls, name, type);
- if (field == 0) {
- THROW(*this, "Error in accessing field definition");
- return JNIObject<jobject>(*this, NULL);
- }
-
- JNIObject<jobjectArray> array(*this, (jobjectArray)mEnv->GetObjectField(obj, field));
- JNIObject<jobject> elem(*this, mEnv->GetObjectArrayElement(array, index));
- if (elem.isNull()) {
- THROW(*this, "Error in accessing index element");
- return JNIObject<jobject>(*this, NULL);
- }
- return elem;
-}
-
-void JNIHelper::setIntField(jobject obj, const char *name, jint value)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- if (cls == NULL) {
- THROW(*this, "Error in accessing class");
- return;
- }
-
- jfieldID field = mEnv->GetFieldID(cls, name, "I");
- if (field == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- mEnv->SetIntField(obj, field, value);
-}
-
-void JNIHelper::setByteField(jobject obj, const char *name, jbyte value)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- if (cls == NULL) {
- THROW(*this, "Error in accessing class");
- return;
- }
-
- jfieldID field = mEnv->GetFieldID(cls, name, "B");
- if (field == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- mEnv->SetByteField(obj, field, value);
-}
-
-void JNIHelper::setBooleanField(jobject obj, const char *name, jboolean value)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- if (cls == NULL) {
- THROW(*this, "Error in accessing class");
- return;
- }
-
- jfieldID field = mEnv->GetFieldID(cls, name, "Z");
- if (field == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- mEnv->SetBooleanField(obj, field, value);
-}
-
-void JNIHelper::setLongField(jobject obj, const char *name, jlong value)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- if (cls == NULL) {
- THROW(*this, "Error in accessing class");
- return;
- }
-
- jfieldID field = mEnv->GetFieldID(cls, name, "J");
- if (field == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- mEnv->SetLongField(obj, field, value);
-}
-
-void JNIHelper::setStaticLongField(jobject obj, const char *name, jlong value)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- if (cls == NULL) {
- THROW(*this, "Error in accessing class");
- return;
- }
-
- setStaticLongField(cls, name, value);
-}
-
-void JNIHelper::setStaticLongField(jclass cls, const char *name, jlong value)
-{
- jfieldID field = mEnv->GetStaticFieldID(cls, name, "J");
- if (field == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- mEnv->SetStaticLongField(cls, field, value);
-}
-
-void JNIHelper::setLongArrayField(jobject obj, const char *name, jlongArray value)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- if (cls == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- jfieldID field = mEnv->GetFieldID(cls, name, "[J");
- if (field == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- mEnv->SetObjectField(obj, field, value);
-}
-
-void JNIHelper::setStaticLongArrayField(jobject obj, const char *name, jlongArray value)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- if (cls == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- setStaticLongArrayField(cls, name, value);
-}
-
-void JNIHelper::setStaticLongArrayField(jclass cls, const char *name, jlongArray value)
-{
- jfieldID field = mEnv->GetStaticFieldID(cls, name, "[J");
- if (field == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- mEnv->SetStaticObjectField(cls, field, value);
- ALOGD("array field set");
-}
-
-void JNIHelper::setLongArrayElement(jobject obj, const char *name, int index, jlong value)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- if (cls == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- jfieldID field = mEnv->GetFieldID(cls, name, "[J");
- if (field == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- JNIObject<jlongArray> array(*this, (jlongArray)mEnv->GetObjectField(obj, field));
- if (array == NULL) {
- THROW(*this, "Error in accessing array");
- return;
- }
-
- jlong *elem = mEnv->GetLongArrayElements(array, NULL);
- if (elem == NULL) {
- THROW(*this, "Error in accessing index element");
- return;
- }
-
- elem[index] = value;
- mEnv->ReleaseLongArrayElements(array, elem, 0);
-}
-
-void JNIHelper::setObjectField(jobject obj, const char *name, const char *type, jobject value)
-{
- JNIObject<jclass> cls(*this, mEnv->GetObjectClass(obj));
- if (cls == NULL) {
- THROW(*this, "Error in accessing class");
- return;
- }
-
- jfieldID field = mEnv->GetFieldID(cls, name, type);
- if (field == NULL) {
- THROW(*this, "Error in accessing field");
- return;
- }
-
- mEnv->SetObjectField(obj, field, value);
-}
-
-jboolean JNIHelper::setStringField(jobject obj, const char *name, const char *value)
-{
- JNIObject<jstring> str(*this, mEnv->NewStringUTF(value));
-
- if (mEnv->ExceptionCheck()) {
- mEnv->ExceptionDescribe();
- mEnv->ExceptionClear();
- return false;
- }
-
- if (str == NULL) {
- THROW(*this, "Error creating string");
- return false;
- }
-
- setObjectField(obj, name, "Ljava/lang/String;", str);
- return true;
-}
-
-void JNIHelper::reportEvent(jclass cls, const char *method, const char *signature, ...)
-{
- va_list params;
- va_start(params, signature);
-
- jmethodID methodID = mEnv->GetStaticMethodID(cls, method, signature);
- if (methodID == 0) {
- ALOGE("Error in getting method ID");
- return;
- }
-
- mEnv->CallStaticVoidMethodV(cls, methodID, params);
- if (mEnv->ExceptionCheck()) {
- mEnv->ExceptionDescribe();
- mEnv->ExceptionClear();
- }
-
- va_end(params);
-}
-
-void JNIHelper::callMethod(jobject obj, const char *method, const char *signature, ...)
-{
- va_list params;
- va_start(params, signature);
-
- jclass cls = mEnv->GetObjectClass(obj);
- jmethodID methodID = mEnv->GetMethodID(cls, method, signature);
- if (methodID == 0) {
- ALOGE("Error in getting method ID");
- return;
- }
-
- mEnv->CallVoidMethodV(obj, methodID, params);
- if (mEnv->ExceptionCheck()) {
- mEnv->ExceptionDescribe();
- mEnv->ExceptionClear();
- }
-
- va_end(params);
-}
-
-jboolean JNIHelper::callStaticMethod(jclass cls, const char *method, const char *signature, ...)
-{
- va_list params;
- va_start(params, signature);
-
- jmethodID methodID = mEnv->GetStaticMethodID(cls, method, signature);
- if (methodID == 0) {
- ALOGE("Error in getting method ID");
- return false;
- }
-
- jboolean result = mEnv->CallStaticBooleanMethodV(cls, methodID, params);
- if (mEnv->ExceptionCheck()) {
- mEnv->ExceptionDescribe();
- mEnv->ExceptionClear();
- return false;
- }
-
- va_end(params);
- return result;
-}
-
-JNIObject<jobject> JNIHelper::createObject(const char *className) {
- return createObjectWithArgs(className, "()V");
-}
-
-JNIObject<jobject> JNIHelper::createObjectWithArgs(
- const char *className, const char *signature, ...)
-{
- va_list params;
- va_start(params, signature);
-
- JNIObject<jclass> cls(*this, mEnv->FindClass(className));
- if (cls == NULL) {
- ALOGE("Error in finding class %s", className);
- return JNIObject<jobject>(*this, NULL);
- }
-
- jmethodID constructor = mEnv->GetMethodID(cls, "<init>", signature);
- if (constructor == 0) {
- ALOGE("Error in constructor ID for %s", className);
- 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);
- return JNIObject<jobject>(*this, NULL);
- }
-
- va_end(params);
- return obj;
-}
-
-JNIObject<jobjectArray> JNIHelper::createObjectArray(const char *className, int num)
-{
- JNIObject<jclass> cls(*this, mEnv->FindClass(className));
- if (cls == NULL) {
- ALOGE("Error in finding class %s", className);
- return JNIObject<jobjectArray>(*this, NULL);
- }
-
- JNIObject<jobject> array(*this, mEnv->NewObjectArray(num, cls.get(), NULL));
- if (array.get() == NULL) {
- ALOGE("Error in creating array of class %s", className);
- return JNIObject<jobjectArray>(*this, NULL);
- }
-
- return JNIObject<jobjectArray>(*this, (jobjectArray)array.detach());
-}
-
-JNIObject<jobject> JNIHelper::getObjectArrayElement(jobjectArray array, int index)
-{
- return JNIObject<jobject>(*this, mEnv->GetObjectArrayElement(array, index));
-}
-
-JNIObject<jobject> JNIHelper::getObjectArrayElement(jobject array, int index)
-{
- return getObjectArrayElement((jobjectArray)array, index);
-}
-
-int JNIHelper::getArrayLength(jarray array) {
- return mEnv->GetArrayLength(array);
-}
-
-JNIObject<jobjectArray> JNIHelper::newObjectArray(int num, const char *className, jobject val) {
- JNIObject<jclass> cls(*this, mEnv->FindClass(className));
- if (cls == NULL) {
- ALOGE("Error in finding class %s", className);
- return JNIObject<jobjectArray>(*this, NULL);
- }
-
- return JNIObject<jobjectArray>(*this, mEnv->NewObjectArray(num, cls, val));
+ mEnv->DeleteLocalRef(obj);
}
JNIObject<jbyteArray> JNIHelper::newByteArray(int num) {
return JNIObject<jbyteArray>(*this, mEnv->NewByteArray(num));
}
-JNIObject<jintArray> JNIHelper::newIntArray(int num) {
- return JNIObject<jintArray>(*this, mEnv->NewIntArray(num));
-}
-
-JNIObject<jlongArray> JNIHelper::newLongArray(int num) {
- return JNIObject<jlongArray>(*this, mEnv->NewLongArray(num));
-}
-
-JNIObject<jstring> JNIHelper::newStringUTF(const char *utf) {
- return JNIObject<jstring>(*this, mEnv->NewStringUTF(utf));
-}
-
-void JNIHelper::setObjectArrayElement(jobjectArray array, int index, jobject obj) {
- mEnv->SetObjectArrayElement(array, index, obj);
-}
-
void JNIHelper::setByteArrayRegion(jbyteArray array, int from, int to, const jbyte *bytes) {
mEnv->SetByteArrayRegion(array, from, to, bytes);
}
-
-void JNIHelper::setIntArrayRegion(jintArray array, int from, int to, const jint *ints) {
- mEnv->SetIntArrayRegion(array, from, to, ints);
-}
-
-void JNIHelper::setLongArrayRegion(jlongArray array, int from, int to, const jlong *longs) {
- mEnv->SetLongArrayRegion(array, from, to, longs);
-}
-
}; // namespace android
-
-
diff --git a/service/jni/jni_helper.h b/service/jni/jni_helper.h
index bb65f1c..febb901 100644
--- a/service/jni/jni_helper.h
+++ b/service/jni/jni_helper.h
@@ -60,7 +60,7 @@
private:
template<typename T2>
- JNIObject(const JNIObject<T2>& rhs);
+ JNIObject(const JNIObject<T2>& rhs); // NOLINT(implicit)
};
class JNIHelper {
@@ -68,111 +68,52 @@
JNIEnv *mEnv;
public :
- JNIHelper(JavaVM *vm);
- JNIHelper(JNIEnv *env);
+ explicit JNIHelper(JavaVM *vm);
+ explicit JNIHelper(JNIEnv *env);
~JNIHelper();
- void throwException(const char *message, int line);
-
- /* helpers to deal with members */
- jboolean getBoolField(jobject obj, const char *name);
- jint getIntField(jobject obj, const char *name);
- jlong getLongField(jobject obj, const char *name);
- JNIObject<jstring> getStringField(jobject obj, const char *name);
- bool getStringFieldValue(jobject obj, const char *name, char *buf, int size);
- JNIObject<jobject> getObjectField(jobject obj, const char *name, const char *type);
- JNIObject<jobjectArray> getArrayField(jobject obj, const char *name, const char *type);
- void getByteArrayField(jobject obj, const char *name, byte* buf, int size);
- jlong getLongArrayField(jobject obj, const char *name, int index);
- JNIObject<jobject> getObjectArrayField(
- jobject obj, const char *name, const char *type, int index);
- void setIntField(jobject obj, const char *name, jint value);
- void setByteField(jobject obj, const char *name, jbyte value);
- jbyte getByteField(jobject obj, const char *name);
- void setBooleanField(jobject obj, const char *name, jboolean value);
- void setLongField(jobject obj, const char *name, jlong value);
- void setLongArrayField(jobject obj, const char *name, jlongArray value);
- void setLongArrayElement(jobject obj, const char *name, int index, jlong value);
- jboolean setStringField(jobject obj, const char *name, const char *value);
- void reportEvent(jclass cls, const char *method, const char *signature, ...);
- JNIObject<jobject> createObject(const char *className);
- JNIObject<jobject> createObjectWithArgs(const char *className, const char *signature, ...);
- JNIObject<jobjectArray> createObjectArray(const char *className, int size);
- void setObjectField(jobject obj, const char *name, const char *type, jobject value);
- void callMethod(jobject obj, const char *method, const char *signature, ...);
-
/* helpers to deal with static members */
- jlong getStaticLongField(jobject obj, const char *name);
- jlong getStaticLongField(jclass cls, const char *name);
- void setStaticLongField(jobject obj, const char *name, jlong value);
- void setStaticLongField(jclass cls, const char *name, jlong value);
- jlong getStaticLongArrayField(jobject obj, const char *name, int index);
- jlong getStaticLongArrayField(jclass cls, const char *name, int index);
- void setStaticLongArrayField(jobject obj, const char *name, jlongArray value);
- void setStaticLongArrayField(jclass obj, const char *name, jlongArray value);
- jboolean callStaticMethod(jclass cls, const char *method, const char *signature, ...);
-
- JNIObject<jobject> getObjectArrayElement(jobjectArray array, int index);
- JNIObject<jobject> getObjectArrayElement(jobject array, int index);
- int getArrayLength(jarray array);
- JNIObject<jobjectArray> newObjectArray(int num, const char *className, jobject val);
JNIObject<jbyteArray> newByteArray(int num);
- JNIObject<jintArray> newIntArray(int num);
- JNIObject<jlongArray> newLongArray(int num);
- JNIObject<jstring> newStringUTF(const char *utf);
- void setObjectArrayElement(jobjectArray array, int index, jobject obj);
void setByteArrayRegion(jbyteArray array, int from, int to, const jbyte *bytes);
- void setIntArrayRegion(jintArray array, int from, int to, const jint *ints);
- void setLongArrayRegion(jlongArray array, int from, int to, const jlong *longs);
-
- jobject newGlobalRef(jobject obj);
- void deleteGlobalRef(jobject obj);
private:
/* Jni wrappers */
- friend class JNIObject<jobject>;
- friend class JNIObject<jstring>;
- friend class JNIObject<jobjectArray>;
- friend class JNIObject<jclass>;
- friend class JNIObject<jlongArray>;
friend class JNIObject<jbyteArray>;
- friend class JNIObject<jintArray>;
jobject newLocalRef(jobject obj);
void deleteLocalRef(jobject obj);
};
template<typename T>
JNIObject<T>::JNIObject(JNIHelper &helper, T obj)
- : mHelper(helper), mObj(obj)
+ : mHelper(helper), mObj(obj)
{ }
template<typename T>
JNIObject<T>::JNIObject(const JNIObject<T>& rhs)
- : mHelper(rhs.mHelper), mObj(NULL)
+ : mHelper(rhs.mHelper), mObj(NULL)
{
- mObj = (T)mHelper.newLocalRef(rhs.mObj);
+ mObj = (T)mHelper.newLocalRef(rhs.mObj);
}
template<typename T>
JNIObject<T>::~JNIObject() {
- release();
+ release();
}
template<typename T>
void JNIObject<T>::release()
{
- if (mObj != NULL) {
- mHelper.deleteLocalRef(mObj);
- mObj = NULL;
- }
+ if (mObj != NULL) {
+ mHelper.deleteLocalRef(mObj);
+ mObj = NULL;
+ }
}
template<typename T>
T JNIObject<T>::clone()
{
- return mHelper.newLocalRef(mObj);
+ return mHelper.newLocalRef(mObj);
}
-
}
#define THROW(env, message) (env).throwException(message, __LINE__)
diff --git a/service/jni/wifi_hal_stub.h b/service/jni/wifi_hal_stub.h
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/service/proto/wifi.proto b/service/proto/wifi.proto
deleted file mode 100644
index 8128ec1..0000000
--- a/service/proto/wifi.proto
+++ /dev/null
@@ -1,412 +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.
- */
-
-syntax = "proto2";
-
-package clearcut.connectivity;
-
-option java_package = "com.android.server.wifi";
-option java_outer_classname = "WifiMetricsProto";
-
-// The information about the Wifi events.
-message WifiLog {
-
- // Session information that gets logged for every Wifi connection.
- repeated ConnectionEvent connection_event = 1;
-
- // Number of saved networks in the user profile.
- optional int32 num_saved_networks = 2;
-
- // Number of open networks in the saved networks.
- optional int32 num_open_networks = 3;
-
- // Number of personal networks.
- optional int32 num_personal_networks = 4;
-
- // Number of enterprise networks.
- optional int32 num_enterprise_networks = 5;
-
- // Does the user have location setting enabled.
- optional bool is_location_enabled = 6;
-
- // Does the user have scanning enabled.
- optional bool is_scanning_always_enabled = 7;
-
- // Number of times user toggled wifi using the settings menu.
- optional int32 num_wifi_toggled_via_settings = 8;
-
- // Number of times user toggled wifi using the airplane menu.
- optional int32 num_wifi_toggled_via_airplane = 9;
-
- // Number of networks added by the user.
- optional int32 num_networks_added_by_user = 10;
-
- // Number of networks added by applications.
- optional int32 num_networks_added_by_apps = 11;
-
- // Number scans that returned empty results.
- optional int32 num_empty_scan_results = 12;
-
- // Number scans that returned at least one result.
- optional int32 num_non_empty_scan_results = 13;
-
- // Number of scans that were one time.
- optional int32 num_oneshot_scans = 14;
-
- // Number of repeated background scans that were scheduled to the chip.
- optional int32 num_background_scans = 15;
-
- // Error codes that a scan can result in.
- enum ScanReturnCode {
-
- // Return Code is unknown.
- SCAN_UNKNOWN = 0;
-
- // Scan was successful.
- SCAN_SUCCESS = 1;
-
- // Scan was successfully started, but was interrupted.
- SCAN_FAILURE_INTERRUPTED = 2;
-
- // Scan failed to start because of invalid configuration
- // (bad channel, etc).
- SCAN_FAILURE_INVALID_CONFIGURATION = 3;
-
- // Could not start a scan because wifi is disabled.
- FAILURE_WIFI_DISABLED = 4;
-
- }
-
- // Mapping of error codes to the number of times that scans resulted
- // in that error.
- repeated ScanReturnEntry scan_return_entries = 16;
-
- message ScanReturnEntry {
-
- // Return code of the scan.
- optional ScanReturnCode scan_return_code = 1;
-
- // Number of entries that were found in the scan.
- optional int32 scan_results_count = 2;
- }
-
- // State of the Wifi.
- enum WifiState {
-
- // State is unknown.
- WIFI_UNKNOWN = 0;
-
- // Wifi is disabled.
- WIFI_DISABLED = 1;
-
- // Wifi is enabled.
- WIFI_DISCONNECTED = 2;
-
- // Wifi is enabled and associated with an AP.
- WIFI_ASSOCIATED = 3;
- }
-
- // Mapping of system state to the number of times that scans were requested in
- // that state
- repeated WifiSystemStateEntry wifi_system_state_entries = 17;
-
- message WifiSystemStateEntry {
-
- // Current WiFi state.
- optional WifiState wifi_state = 1;
-
- // Count of scans in state.
- optional int32 wifi_state_count = 2;
-
- // Is screen on.
- optional bool is_screen_on = 3;
- }
-
- // Mapping of Error/Success codes to the number of background scans that resulted in it
- repeated ScanReturnEntry background_scan_return_entries = 18;
-
- // Mapping of system state to the number of times that Background scans were requested in that
- // state
- repeated WifiSystemStateEntry background_scan_request_state = 19;
-
- // Total number of times the Watchdog of Last Resort triggered, resetting the wifi stack
- optional int32 num_last_resort_watchdog_triggers = 20;
-
- // Total number of networks over bad association threshold when watchdog triggered
- optional int32 num_last_resort_watchdog_bad_association_networks_total = 21;
-
- // Total number of networks over bad authentication threshold when watchdog triggered
- optional int32 num_last_resort_watchdog_bad_authentication_networks_total = 22;
-
- // Total number of networks over bad dhcp threshold when watchdog triggered
- optional int32 num_last_resort_watchdog_bad_dhcp_networks_total = 23;
-
- // Total number of networks over bad other threshold when watchdog triggered
- optional int32 num_last_resort_watchdog_bad_other_networks_total = 24;
-
- // Total count of networks seen when watchdog triggered
- optional int32 num_last_resort_watchdog_available_networks_total = 25;
-
- // Total count of triggers with atleast one bad association network
- optional int32 num_last_resort_watchdog_triggers_with_bad_association = 26;
-
- // Total count of triggers with atleast one bad authentication network
- optional int32 num_last_resort_watchdog_triggers_with_bad_authentication = 27;
-
- // Total count of triggers with atleast one bad dhcp network
- optional int32 num_last_resort_watchdog_triggers_with_bad_dhcp = 28;
-
- // Total count of triggers with atleast one bad other network
- optional int32 num_last_resort_watchdog_triggers_with_bad_other = 29;
-
- // Count of times connectivity watchdog confirmed pno is working
- optional int32 num_connectivity_watchdog_pno_good = 30;
-
- // Count of times connectivity watchdog found pno not working
- optional int32 num_connectivity_watchdog_pno_bad = 31;
-
- // Count of times connectivity watchdog confirmed background scan is working
- optional int32 num_connectivity_watchdog_background_good = 32;
-
- // Count of times connectivity watchdog found background scan not working
- optional int32 num_connectivity_watchdog_background_bad = 33;
-
- // The time duration represented by this wifi log, from start to end of capture
- optional int32 record_duration_sec = 34;
-
- // Counts the occurrences of each individual RSSI poll level
- repeated RssiPollCount rssi_poll_rssi_count = 35;
-
- // Total number of times WiFi connected immediately after a Last Resort Watchdog trigger,
- // without new networks becoming available.
- optional int32 num_last_resort_watchdog_successes = 36;
-
- // Counts the occurrences of each alert reason.
- repeated AlertReasonCount alert_reason_count = 47;
-
- // Total number of saved hidden networks
- optional int32 num_hidden_networks = 37;
-
- // Total number of saved passpoint / hotspot 2.0 networks
- optional int32 num_passpoint_networks = 38;
-
- // Total number of scan results
- optional int32 num_total_scan_results = 39;
-
- // Total number of scan results for open networks
- optional int32 num_open_network_scan_results = 40;
-
- // Total number of scan results for personal networks
- optional int32 num_personal_network_scan_results = 41;
-
- // Total number of scan results for enterprise networks
- optional int32 num_enterprise_network_scan_results = 42;
-
- // Total number of scan results for hidden networks
- optional int32 num_hidden_network_scan_results = 43;
-
- // Total number of scan results for hotspot 2.0 r1 networks
- optional int32 num_hotspot2_r1_network_scan_results = 44;
-
- // Total number of scan results for hotspot 2.0 r2 networks
- optional int32 num_hotspot2_r2_network_scan_results = 45;
-
- // Total number of scans handled by framework (oneshot or otherwise)
- optional int32 num_scans = 46;
-
- // Counts the occurrences of each Wifi score
- repeated WifiScoreCount wifi_score_count = 48;
-}
-
-// Information that gets logged for every WiFi connection.
-message RouterFingerPrint {
-
- enum RoamType {
-
- // Type is unknown.
- ROAM_TYPE_UNKNOWN = 0;
-
- // No roaming - usually happens on a single band (2.4 GHz) router.
- ROAM_TYPE_NONE = 1;
-
- // Enterprise router.
- ROAM_TYPE_ENTERPRISE = 2;
-
- // DBDC => Dual Band Dual Concurrent essentially a router that
- // supports both 2.4 GHz and 5 GHz bands.
- ROAM_TYPE_DBDC = 3;
- }
-
- enum Auth {
-
- // Auth is unknown.
- AUTH_UNKNOWN = 0;
-
- // No authentication.
- AUTH_OPEN = 1;
-
- // If the router uses a personal authentication.
- AUTH_PERSONAL = 2;
-
- // If the router is setup for enterprise authentication.
- AUTH_ENTERPRISE = 3;
- }
-
- enum RouterTechnology {
-
- // Router is unknown.
- ROUTER_TECH_UNKNOWN = 0;
-
- // Router Channel A.
- ROUTER_TECH_A = 1;
-
- // Router Channel B.
- ROUTER_TECH_B = 2;
-
- // Router Channel G.
- ROUTER_TECH_G = 3;
-
- // Router Channel N.
- ROUTER_TECH_N = 4;
-
- // Router Channel AC.
- ROUTER_TECH_AC = 5;
-
- // When the channel is not one of the above.
- ROUTER_TECH_OTHER = 6;
- }
-
- optional RoamType roam_type = 1;
-
- // Channel on which the connection takes place.
- optional int32 channel_info = 2;
-
- // DTIM setting of the router.
- optional int32 dtim = 3;
-
- // Authentication scheme of the router.
- optional Auth authentication = 4;
-
- // If the router is hidden.
- optional bool hidden = 5;
-
- // Channel information.
- optional RouterTechnology router_technology = 6;
-
- // whether ipv6 is supported.
- optional bool supports_ipv6 = 7;
-
- // If the router is a passpoint / hotspot 2.0 network
- optional bool passpoint = 8;
-}
-
-message ConnectionEvent {
-
- // Roam Type.
- enum RoamType {
-
- // Type is unknown.
- ROAM_UNKNOWN = 0;
-
- // No roaming.
- ROAM_NONE = 1;
-
- // DBDC roaming.
- ROAM_DBDC = 2;
-
- // Enterprise roaming.
- ROAM_ENTERPRISE = 3;
-
- // User selected roaming.
- ROAM_USER_SELECTED = 4;
-
- // Unrelated.
- ROAM_UNRELATED = 5;
- }
-
- // Connectivity Level Failure.
- enum ConnectivityLevelFailure {
-
- // Failure is unknown.
- HLF_UNKNOWN = 0;
-
- // No failure.
- HLF_NONE = 1;
-
- // DHCP failure.
- HLF_DHCP = 2;
-
- // No internet connection.
- HLF_NO_INTERNET = 3;
-
- // No internet connection.
- HLF_UNWANTED = 4;
- }
-
- // Start time of the connection.
- optional int64 start_time_millis = 1;// [(datapol.semantic_type) = ST_TIMESTAMP];
-
- // Duration to connect.
- optional int32 duration_taken_to_connect_millis = 2;
-
- // Router information.
- optional RouterFingerPrint router_fingerprint = 3;
-
- // RSSI at the start of the connection.
- optional int32 signal_strength = 4;
-
- // Roam Type.
- optional RoamType roam_type = 5;
-
- // Result of the connection.
- optional int32 connection_result = 6;
-
- // Reasons for level 2 failure (needs to be coordinated with wpa-supplicant).
- optional int32 level_2_failure_code = 7;
-
- // Failures that happen at the connectivity layer.
- optional ConnectivityLevelFailure connectivity_level_failure_code = 8;
-
- // Has bug report been taken.
- optional bool automatic_bug_report_taken = 9;
-}
-
-// Number of occurrences of a specific RSSI poll rssi value
-message RssiPollCount {
- // RSSI
- optional int32 rssi = 1;
-
- // Number of RSSI polls with 'rssi'
- optional int32 count = 2;
-}
-
-// Number of occurrences of a specific alert reason value
-message AlertReasonCount {
- // Alert reason
- optional int32 reason = 1;
-
- // Number of alerts with |reason|.
- optional int32 count = 2;
-}
-
-// Counts the number of instances of a specific Wifi Score calculated by WifiScoreReport
-message WifiScoreCount {
- // Wifi Score
- optional int32 score = 1;
-
- // Number of Wifi score reports with this score
- optional int32 count = 2;
-}
diff --git a/service/wifi-events.rc b/service/wifi-events.rc
new file mode 100644
index 0000000..b4b0934
--- /dev/null
+++ b/service/wifi-events.rc
@@ -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.
+#
+
+on fs
+ setprop sys.wifitracing.started 0
+
+on property:sys.boot_completed=1 && property:sys.wifitracing.started=0
+ # Create trace buffer, and set basic configuration.
+ mkdir /sys/kernel/debug/tracing/instances/wifi 711
+ restorecon_recursive /sys/kernel/debug/tracing/instances/wifi
+ write /sys/kernel/debug/tracing/instances/wifi/tracing_on 0
+ write /sys/kernel/debug/tracing/instances/wifi/buffer_size_kb 1
+ write /sys/kernel/debug/tracing/instances/wifi/trace_options disable_on_free
+
+ # Enable cfg80211 events for connection and key management events.
+ # - Events are not actually logged until WifiService writes "1" to
+ # /sys/kernel/debug/tracing/instances/wifi/tracing_on.
+ # - WifiService is responsible for turning tracing off and on.
+ write /sys/kernel/debug/tracing/instances/wifi/events/cfg80211/cfg80211_gtk_rekey_notify/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/cfg80211/rdev_add_key/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/cfg80211/rdev_assoc/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/cfg80211/rdev_auth/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/cfg80211/rdev_connect/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/cfg80211/rdev_set_default_key/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/cfg80211/rdev_set_default_mgmt_key/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/cfg80211/rdev_set_rekey_data/enable 1
+
+ # Enable datapath events for Wifi.
+ # - Events are not actually logged until WifiService writes "1" to
+ # /sys/kernel/debug/tracing/instances/wifi/tracing_on.
+ # - WifiService will ensure that tracing is turned back off,
+ # when a connection attempt ends (whether in success or failure)
+ write /sys/kernel/debug/tracing/instances/wifi/events/net/filter name==${wifi.interface:-wlan0}
+ write /sys/kernel/debug/tracing/instances/wifi/events/net/net_dev_queue/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/net/net_dev_xmit/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/net/netif_rx/enable 1
+ write /sys/kernel/debug/tracing/instances/wifi/events/net/netif_receive_skb/enable 1
+
+ # Set DAC to allow system_server to enable/disable, and read wifi trace
+ # events.
+ chown system /sys/kernel/debug/tracing/instances/wifi/tracing_on
+ chown system /sys/kernel/debug/tracing/instances/wifi/free_buffer
+ chown system /sys/kernel/debug/tracing/instances/wifi/trace
+ chmod 200 /sys/kernel/debug/tracing/instances/wifi/tracing_on
+ chmod 400 /sys/kernel/debug/tracing/instances/wifi/free_buffer
+ chmod 600 /sys/kernel/debug/tracing/instances/wifi/trace
+ setprop sys.wifitracing.started 1
+
+on property:sys.boot_completed=1 && property:wifi.interface=* && sys.wifitracing.started=1
+ # Override default value.
+ write /sys/kernel/debug/tracing/instances/wifi/events/net/filter name==${wifi.interface}
diff --git a/tests/wifitests/Android.mk b/tests/wifitests/Android.mk
index 4904bda..94f3435 100644
--- a/tests/wifitests/Android.mk
+++ b/tests/wifitests/Android.mk
@@ -14,62 +14,13 @@
LOCAL_PATH:= $(call my-dir)
-# Make mock HAL library
-# ============================================================
-
-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 += \
- $(JNI_H_INCLUDE) \
- $(LOCAL_PATH)/../../service/jni \
- $(call include-path-for, libhardware)/hardware \
- $(call include-path-for, libhardware_legacy)/hardware_legacy \
- packages/apps/Test/connectivity/sl4n/rapidjson/include \
- libcore/include
-
-LOCAL_SRC_FILES := \
- jni/wifi_hal_mock.cpp
-
-ifdef INCLUDE_NAN_FEATURE
-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
-
-include $(BUILD_SHARED_LIBRARY)
-
# Make test APK
# ============================================================
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-RESOURCE_FILES := $(call all-named-files-under, R.java, $(intermediates.COMMON))
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files) \
- $RESOURCE_FILES
-
-ifndef INCLUDE_NAN_FEATURE
-LOCAL_SRC_FILES := $(filter-out $(call all-java-files-under, \
- src/com/android/server/wifi/nan),$(LOCAL_SRC_FILES))
-endif
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Provide jack a list of classes to exclude form code coverage
# This list is generated from the java source files in this module
@@ -93,11 +44,14 @@
# These patterns will match all classes in this module and their inner classes.
jacoco_exclude := $(subst $(space),$(comma),$(patsubst %,%*,$(local_classes)))
-jacoco_include := com.android.server.wifi.*,android.net.wifi.*
+jacoco_include := com.android.server.wifi.*
LOCAL_JACK_COVERAGE_INCLUDE_FILTER := $(jacoco_include)
LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
+LOCAL_DX_FLAGS := --multi-dex
+LOCAL_JACK_FLAGS := --multi-dex native
+
# wifi-service and services must be included here so that the latest changes
# will be used when tests. Otherwise the tests would run against the installed
# system.
@@ -105,7 +59,8 @@
# since neither is declared a static java library.
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
- mockito-target \
+ mockito-target-minus-junit4 \
+ frameworks-base-testutils \
services \
wifi-service \
@@ -113,30 +68,50 @@
android.test.runner \
wifi-service \
services \
+ android.hidl.manager-V1.0-java
# 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-system \
+ libui \
+ libunwind \
+ libutils \
+ libvndksupport \
ifdef WPA_SUPPLICANT_VERSION
LOCAL_JNI_SHARED_LIBRARIES += libwpa_client
endif
LOCAL_PACKAGE_NAME := FrameworksWifiTests
-LOCAL_JNI_SHARED_LIBRARIES += libwifi-hal-mock
include $(BUILD_PACKAGE)
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/coverage.sh b/tests/wifitests/coverage.sh
index 3480541..bf50dee 100755
--- a/tests/wifitests/coverage.sh
+++ b/tests/wifitests/coverage.sh
@@ -56,5 +56,4 @@
--coverage-file $COVERAGE_OUTPUT_FILE \
--report-type $REPORT_TYPE \
--source-dir $ANDROID_BUILD_TOP/frameworks/opt/net/wifi/tests/wifitests/src \
- --source-dir $ANDROID_BUILD_TOP/frameworks/opt/net/wifi/service/java \
- --source-dir $ANDROID_BUILD_TOP/frameworks/base/wifi/java
+ --source-dir $ANDROID_BUILD_TOP/frameworks/opt/net/wifi/service/java
diff --git a/tests/wifitests/jni/wifi_hal_mock.cpp b/tests/wifitests/jni/wifi_hal_mock.cpp
deleted file mode 100644
index 06b2d23..0000000
--- a/tests/wifitests/jni/wifi_hal_mock.cpp
+++ /dev/null
@@ -1,196 +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 "JniConstants.h"
-#include <ScopedUtfChars.h>
-#include <ScopedBytes.h>
-#include <utils/misc.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <utils/Log.h>
-#include <utils/String16.h>
-#include <ctype.h>
-#include <sys/socket.h>
-#include <linux/if.h>
-#include "wifi.h"
-#include "wifi_hal.h"
-#include "jni_helper.h"
-#include "wifi_hal_mock.h"
-#include <sstream>
-#include <rapidjson/document.h>
-#include <rapidjson/stringbuffer.h>
-#include <rapidjson/writer.h>
-
-namespace android {
-
-jobject mock_mObj; /* saved HalMock object (not class!) */
-JavaVM* mock_mVM = NULL; /* saved JVM pointer */
-
-/* Variable and function declared and defined in:
- * com_android_server_wifi_nan_WifiNanNative.cpp
- */
-extern wifi_hal_fn hal_fn;
-extern "C" jint Java_com_android_server_wifi_WifiNative_registerNatives(
- JNIEnv* env, jclass clazz);
-
-namespace hal_json_tags {
-static constexpr const char* const type_tag = "type";
-static constexpr const char* const value_tag = "value";
-
-static constexpr const char* const type_int_tag = "int";
-static constexpr const char* const type_byte_array_tag = "byte_array";
-}
-
-HalMockJsonWriter::HalMockJsonWriter()
- : allocator(doc.GetAllocator()) {
- doc.SetObject();
-}
-
-void HalMockJsonWriter::put_int(const char* name, int x) {
- rapidjson::Value object(rapidjson::kObjectType);
- object.AddMember(
- rapidjson::Value(hal_json_tags::type_tag,
- strlen(hal_json_tags::type_tag)),
- rapidjson::Value(hal_json_tags::type_int_tag,
- strlen(hal_json_tags::type_int_tag)),
- allocator);
- object.AddMember(
- rapidjson::Value(hal_json_tags::value_tag,
- strlen(hal_json_tags::value_tag)),
- rapidjson::Value(x), allocator);
- doc.AddMember(rapidjson::Value(name, strlen(name)), object, allocator);
-}
-
-void HalMockJsonWriter::put_byte_array(const char* name, u8* byte_array,
- int array_length) {
- rapidjson::Value object(rapidjson::kObjectType);
- object.AddMember(
- rapidjson::Value(hal_json_tags::type_tag,
- strlen(hal_json_tags::type_tag)),
- rapidjson::Value(hal_json_tags::type_byte_array_tag,
- strlen(hal_json_tags::type_byte_array_tag)),
- allocator);
-
- rapidjson::Value array(rapidjson::kArrayType);
- for (int i = 0; i < array_length; ++i) {
- array.PushBack((int) byte_array[i], allocator);
- }
-
- object.AddMember(
- rapidjson::Value(hal_json_tags::value_tag,
- strlen(hal_json_tags::value_tag)),
- array, allocator);
- doc.AddMember(rapidjson::Value(name, strlen(name)), object, allocator);
-}
-
-std::string HalMockJsonWriter::to_string() {
- rapidjson::StringBuffer strbuf;
- rapidjson::Writer < rapidjson::StringBuffer > writer(strbuf);
- doc.Accept(writer);
- return strbuf.GetString();
-}
-
-HalMockJsonReader::HalMockJsonReader(const char* str) {
- doc.Parse(str);
- assert(doc.IsObject());
-}
-
-int HalMockJsonReader::get_int(const char* key, bool* error) {
- if (!doc.HasMember(key)) {
- *error = true;
- ALOGE("get_int: can't find %s key", key);
- return 0;
- }
- const rapidjson::Value& element = doc[key];
- if (!element.HasMember(hal_json_tags::value_tag)) {
- *error = true;
- ALOGE("get_int: can't find the 'value' sub-key for %s key", key);
- return 0;
- }
- const rapidjson::Value& value = element[hal_json_tags::value_tag];
- if (!value.IsInt()) {
- *error = true;
- ALOGE("get_int: the value isn't an 'int' for the %s key", key);
- return 0;
- }
- return value.GetInt();
-}
-
-void HalMockJsonReader::get_byte_array(const char* key, bool* error, u8* array,
- unsigned int max_array_size) {
- if (!doc.HasMember(key)) {
- *error = true;
- ALOGE("get_byte_array: can't find %s key", key);
- return;
- }
- const rapidjson::Value& element = doc[key];
- if (!element.HasMember(hal_json_tags::value_tag)) {
- *error = true;
- ALOGE("get_byte_array: can't find the 'value' sub-key for %s key", key);
- return;
- }
- const rapidjson::Value& value = element[hal_json_tags::value_tag];
- if (!value.IsArray()) {
- *error = true;
- ALOGE("get_byte_array: the value isn't an 'array' for the %s key", key);
- return;
- }
-
- if (value.Size() > max_array_size) {
- *error = true;
- ALOGE("get_byte_array: size of array (%d) is larger than maximum "
- "allocated (%d)",
- value.Size(), max_array_size);
- return;
- }
-
- for (unsigned int i = 0; i < value.Size(); ++i) {
- const rapidjson::Value& item = value[i];
- if (!item.IsInt()) {
- *error = true;
- ALOGE("get_byte_array: the value isn't an 'int' for the %s[%d] key", key,
- i);
- return;
- }
- array[i] = item.GetInt();
- }
-}
-
-
-int init_wifi_hal_func_table_mock(wifi_hal_fn *fn) {
- if (fn == NULL) {
- return -1;
- }
-
- /* TODO: add other Wi-Fi HAL registrations here - once you have mocks */
-
- return 0;
-}
-
-extern "C" jint Java_com_android_server_wifi_HalMockUtils_initHalMock(
- JNIEnv* env, jclass clazz) {
- env->GetJavaVM(&mock_mVM);
-
- Java_com_android_server_wifi_WifiNative_registerNatives(env, clazz);
- return init_wifi_hal_func_table_mock(&hal_fn);
-}
-
-extern "C" void Java_com_android_server_wifi_HalMockUtils_setHalMockObject(
- JNIEnv* env, jclass clazz, jobject hal_mock_object) {
- mock_mObj = (jobject) env->NewGlobalRef(hal_mock_object);
-}
-
-} // namespace android
diff --git a/tests/wifitests/jni/wifi_hal_mock.h b/tests/wifitests/jni/wifi_hal_mock.h
deleted file mode 100644
index 2b9cad4..0000000
--- a/tests/wifitests/jni/wifi_hal_mock.h
+++ /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.
- */
-
-#ifndef __WIFI_HAL_MOCK_H__
-#define __WIFI_HAL_MOCK_H__
-
-#include "wifi_hal.h"
-
-#include <rapidjson/document.h>
-#include <rapidjson/stringbuffer.h>
-#include <rapidjson/writer.h>
-
-namespace android {
-
-class HalMockJsonWriter {
- public:
- HalMockJsonWriter();
-
- void put_int(const char* name, int x);
-
- void put_byte_array(const char* name, u8* byte_array, int array_length);
-
- std::string to_string();
-
- private:
- rapidjson::Document doc;
- rapidjson::Document::AllocatorType& allocator;
-};
-
-class HalMockJsonReader {
- public:
- HalMockJsonReader(const char* str);
-
- int get_int(const char* key, bool* error);
-
- void get_byte_array(const char* key, bool* error, u8* array,
- unsigned int max_array_size);
- private:
- rapidjson::Document doc;
-};
-
-/* declare all HAL mock APIs here*/
-wifi_error wifi_nan_enable_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanEnableRequest* msg);
-wifi_error wifi_nan_disable_request_mock(transaction_id id,
- wifi_interface_handle iface);
-wifi_error wifi_nan_publish_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanPublishRequest* msg);
-wifi_error wifi_nan_publish_cancel_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanPublishCancelRequest* msg);
-wifi_error wifi_nan_subscribe_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanSubscribeRequest* msg);
-wifi_error wifi_nan_subscribe_cancel_request_mock(
- transaction_id id, wifi_interface_handle iface,
- NanSubscribeCancelRequest* msg);
-wifi_error wifi_nan_transmit_followup_request_mock(
- transaction_id id, wifi_interface_handle iface,
- NanTransmitFollowupRequest* msg);
-wifi_error wifi_nan_stats_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanStatsRequest* msg);
-wifi_error wifi_nan_config_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanConfigRequest* msg);
-wifi_error wifi_nan_tca_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanTCARequest* msg);
-wifi_error wifi_nan_beacon_sdf_payload_request_mock(
- transaction_id id, wifi_interface_handle iface,
- NanBeaconSdfPayloadRequest* msg);
-wifi_error wifi_nan_register_handler_mock(wifi_interface_handle iface,
- NanCallbackHandler handlers);
-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);
-
-} // namespace android
-
-#endif //__WIFI_HAL_MOCK_H__
diff --git a/tests/wifitests/jni/wifi_nan_hal_mock.cpp b/tests/wifitests/jni/wifi_nan_hal_mock.cpp
deleted file mode 100644
index 022fadd..0000000
--- a/tests/wifitests/jni/wifi_nan_hal_mock.cpp
+++ /dev/null
@@ -1,582 +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 "JniConstants.h"
-#include <ScopedUtfChars.h>
-#include <ScopedBytes.h>
-#include <utils/misc.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <utils/Log.h>
-#include <utils/String16.h>
-#include <ctype.h>
-#include <sys/socket.h>
-#include <linux/if.h>
-#include "wifi.h"
-#include "wifi_hal.h"
-#include "jni_helper.h"
-#include "wifi_hal_mock.h"
-#include <sstream>
-#include <rapidjson/document.h>
-#include <rapidjson/stringbuffer.h>
-#include <rapidjson/writer.h>
-
-namespace android {
-
-extern jobject mock_mObj; /* saved NanHalMock object */
-extern JavaVM* mock_mVM; /* saved JVM pointer */
-
-/* Variable and function declared and defined in:
- * com_android_servier_wifi_nan_WifiNanNative.cpp
- */
-extern wifi_hal_fn hal_fn;
-extern "C" jint Java_com_android_server_wifi_nan_WifiNanNative_registerNanNatives(
- JNIEnv* env, jclass clazz);
-
-static NanCallbackHandler mCallbackHandlers;
-
-wifi_error wifi_nan_enable_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanEnableRequest* msg) {
- JNIHelper helper(mock_mVM);
-
- ALOGD("wifi_nan_enable_request_mock");
- HalMockJsonWriter jsonW;
- jsonW.put_int("master_pref", msg->master_pref);
- jsonW.put_int("cluster_low", msg->cluster_low);
- jsonW.put_int("cluster_high", msg->cluster_high);
- jsonW.put_int("config_support_5g", msg->config_support_5g);
- jsonW.put_int("support_5g_val", msg->support_5g_val);
- jsonW.put_int("config_sid_beacon", msg->config_sid_beacon);
- jsonW.put_int("sid_beacon_val", msg->sid_beacon_val);
- jsonW.put_int("config_2dot4g_rssi_close", msg->config_2dot4g_rssi_close);
- jsonW.put_int("rssi_close_2dot4g_val", msg->rssi_close_2dot4g_val);
- jsonW.put_int("config_2dot4g_rssi_middle", msg->config_2dot4g_rssi_middle);
- jsonW.put_int("rssi_middle_2dot4g_val", msg->rssi_middle_2dot4g_val);
- jsonW.put_int("config_2dot4g_rssi_proximity",
- msg->config_2dot4g_rssi_proximity);
- jsonW.put_int("rssi_proximity_2dot4g_val", msg->rssi_proximity_2dot4g_val);
- jsonW.put_int("config_hop_count_limit", msg->config_hop_count_limit);
- jsonW.put_int("hop_count_limit_val", msg->hop_count_limit_val);
- jsonW.put_int("config_2dot4g_support", msg->config_2dot4g_support);
- jsonW.put_int("support_2dot4g_val", msg->support_2dot4g_val);
- jsonW.put_int("config_2dot4g_beacons", msg->config_2dot4g_beacons);
- jsonW.put_int("beacon_2dot4g_val", msg->beacon_2dot4g_val);
- jsonW.put_int("config_2dot4g_sdf", msg->config_2dot4g_sdf);
- jsonW.put_int("sdf_2dot4g_val", msg->sdf_2dot4g_val);
- jsonW.put_int("config_5g_beacons", msg->config_5g_beacons);
- jsonW.put_int("beacon_5g_val", msg->beacon_5g_val);
- jsonW.put_int("config_5g_sdf", msg->config_5g_sdf);
- jsonW.put_int("sdf_5g_val", msg->sdf_5g_val);
- jsonW.put_int("config_5g_rssi_close", msg->config_5g_rssi_close);
- jsonW.put_int("rssi_close_5g_val", msg->rssi_close_5g_val);
- jsonW.put_int("config_5g_rssi_middle", msg->config_5g_rssi_middle);
- jsonW.put_int("rssi_middle_5g_val", msg->rssi_middle_5g_val);
- 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_oui", msg->config_oui);
- jsonW.put_int("oui_val", msg->oui_val);
- jsonW.put_int("config_intf_addr", msg->config_intf_addr);
- jsonW.put_byte_array("intf_addr_val", msg->intf_addr_val, 6);
- jsonW.put_int("config_cluster_attribute_val",
- msg->config_cluster_attribute_val);
- jsonW.put_int("config_scan_params", msg->config_scan_params);
- jsonW.put_int("scan_params_val.dwell_time.0",
- msg->scan_params_val.dwell_time[NAN_CHANNEL_24G_BAND]);
- jsonW.put_int("scan_params_val.dwell_time.1",
- msg->scan_params_val.dwell_time[NAN_CHANNEL_5G_BAND_LOW]);
- jsonW.put_int("scan_params_val.dwell_time.2",
- msg->scan_params_val.dwell_time[NAN_CHANNEL_5G_BAND_HIGH]);
- jsonW.put_int("scan_params_val.scan_period.0",
- msg->scan_params_val.scan_period[NAN_CHANNEL_24G_BAND]);
- jsonW.put_int("scan_params_val.scan_period.0",
- msg->scan_params_val.scan_period[NAN_CHANNEL_5G_BAND_LOW]);
- jsonW.put_int("scan_params_val.scan_period.0",
- msg->scan_params_val.scan_period[NAN_CHANNEL_5G_BAND_HIGH]);
- 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);
- std::string str = jsonW.to_string();
-
- JNIObject < jstring > json_write_string = helper.newStringUTF(str.c_str());
-
- helper.callMethod(mock_mObj, "enableHalMockNative", "(SLjava/lang/String;)V",
- (short) id, json_write_string.get());
-
- return WIFI_SUCCESS;
-}
-
-wifi_error wifi_nan_disable_request_mock(transaction_id id,
- wifi_interface_handle iface) {
- JNIHelper helper(mock_mVM);
-
- ALOGD("wifi_nan_disable_request_mock");
- helper.callMethod(mock_mObj, "disableHalMockNative", "(S)V", (short) id);
-
- return WIFI_SUCCESS;
-}
-
-wifi_error wifi_nan_publish_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanPublishRequest* msg) {
- JNIHelper helper(mock_mVM);
-
- ALOGD("wifi_nan_publish_request_mock");
- HalMockJsonWriter jsonW;
- jsonW.put_int("publish_id", msg->publish_id);
- jsonW.put_int("ttl", msg->ttl);
- jsonW.put_int("publish_type", msg->publish_type);
- jsonW.put_int("tx_type", msg->tx_type);
- jsonW.put_int("publish_count", msg->publish_count);
- jsonW.put_int("service_name_len", msg->service_name_len);
- jsonW.put_byte_array("service_name", msg->service_name,
- msg->service_name_len);
- jsonW.put_int("publish_match_indicator", msg->publish_match_indicator);
- jsonW.put_int("service_specific_info_len", msg->service_specific_info_len);
- jsonW.put_byte_array("service_specific_info", msg->service_specific_info,
- msg->service_specific_info_len);
- jsonW.put_int("rx_match_filter_len", msg->rx_match_filter_len);
- jsonW.put_byte_array("rx_match_filter", msg->rx_match_filter,
- msg->rx_match_filter_len);
- jsonW.put_int("tx_match_filter_len", msg->tx_match_filter_len);
- jsonW.put_byte_array("tx_match_filter", msg->tx_match_filter,
- msg->tx_match_filter_len);
- jsonW.put_int("rssi_threshold_flag", msg->rssi_threshold_flag);
- jsonW.put_int("connmap", msg->connmap);
- std::string str = jsonW.to_string();
-
- JNIObject < jstring > json_write_string = helper.newStringUTF(str.c_str());
-
- helper.callMethod(mock_mObj, "publishHalMockNative", "(SLjava/lang/String;)V",
- (short) id, json_write_string.get());
- return WIFI_SUCCESS;
-}
-
-wifi_error wifi_nan_publish_cancel_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanPublishCancelRequest* msg) {
- JNIHelper helper(mock_mVM);
-
- ALOGD("wifi_nan_publish_cancel_request_mock");
- HalMockJsonWriter jsonW;
- jsonW.put_int("publish_id", msg->publish_id);
- std::string str = jsonW.to_string();
-
- JNIObject < jstring > json_write_string = helper.newStringUTF(str.c_str());
-
- helper.callMethod(mock_mObj, "publishCancelHalMockNative",
- "(SLjava/lang/String;)V", (short) id,
- json_write_string.get());
- return WIFI_SUCCESS;
-}
-
-wifi_error wifi_nan_subscribe_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanSubscribeRequest* msg) {
- JNIHelper helper(mock_mVM);
-
- ALOGD("wifi_nan_subscribe_request_mock");
- HalMockJsonWriter jsonW;
- jsonW.put_int("subscribe_id", msg->subscribe_id);
- jsonW.put_int("ttl", msg->ttl);
- jsonW.put_int("period", msg->period);
- jsonW.put_int("subscribe_type", msg->subscribe_type);
- jsonW.put_int("serviceResponseFilter", msg->serviceResponseFilter);
- jsonW.put_int("serviceResponseInclude", msg->serviceResponseInclude);
- jsonW.put_int("useServiceResponseFilter", msg->useServiceResponseFilter);
- jsonW.put_int("ssiRequiredForMatchIndication",
- msg->ssiRequiredForMatchIndication);
- jsonW.put_int("subscribe_match_indicator", msg->subscribe_match_indicator);
- jsonW.put_int("subscribe_count", msg->subscribe_count);
- jsonW.put_int("service_name_len", msg->service_name_len);
- jsonW.put_byte_array("service_name", msg->service_name,
- msg->service_name_len);
- jsonW.put_int("service_specific_info_len", msg->service_name_len);
- jsonW.put_byte_array("service_specific_info", msg->service_specific_info,
- msg->service_specific_info_len);
- jsonW.put_int("rx_match_filter_len", msg->rx_match_filter_len);
- jsonW.put_byte_array("rx_match_filter", msg->rx_match_filter,
- msg->rx_match_filter_len);
- jsonW.put_int("tx_match_filter_len", msg->tx_match_filter_len);
- jsonW.put_byte_array("tx_match_filter", msg->tx_match_filter,
- msg->tx_match_filter_len);
- jsonW.put_int("rssi_threshold_flag", msg->rssi_threshold_flag);
- 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);
- std::string str = jsonW.to_string();
-
- JNIObject < jstring > json_write_string = helper.newStringUTF(str.c_str());
-
- helper.callMethod(mock_mObj, "subscribeHalMockNative",
- "(SLjava/lang/String;)V", (short) id,
- json_write_string.get());
- return WIFI_SUCCESS;
-}
-
-wifi_error wifi_nan_subscribe_cancel_request_mock(
- transaction_id id, wifi_interface_handle iface,
- NanSubscribeCancelRequest* msg) {
- JNIHelper helper(mock_mVM);
-
- ALOGD("wifi_nan_subscribe_cancel_request_mock");
- HalMockJsonWriter jsonW;
- jsonW.put_int("subscribe_id", msg->subscribe_id);
- std::string str = jsonW.to_string();
-
- JNIObject < jstring > json_write_string = helper.newStringUTF(str.c_str());
-
- helper.callMethod(mock_mObj, "subscribeCancelHalMockNative",
- "(SLjava/lang/String;)V", (short) id,
- json_write_string.get());
- return WIFI_SUCCESS;
-}
-
-wifi_error wifi_nan_transmit_followup_request_mock(
- transaction_id id, wifi_interface_handle iface,
- NanTransmitFollowupRequest* msg) {
- JNIHelper helper(mock_mVM);
-
- ALOGD("wifi_nan_transmit_followup_request_mock");
- HalMockJsonWriter jsonW;
- jsonW.put_int("publish_subscribe_id", msg->publish_subscribe_id);
- jsonW.put_int("requestor_instance_id", msg->requestor_instance_id);
- jsonW.put_byte_array("addr", msg->addr, 6);
- jsonW.put_int("priority", msg->priority);
- jsonW.put_int("dw_or_faw", msg->dw_or_faw);
- jsonW.put_int("service_specific_info_len", msg->service_specific_info_len);
- jsonW.put_byte_array("service_specific_info", msg->service_specific_info,
- msg->service_specific_info_len);
-
- std::string str = jsonW.to_string();
-
- JNIObject < jstring > json_write_string = helper.newStringUTF(str.c_str());
-
- helper.callMethod(mock_mObj, "transmitFollowupHalMockNative",
- "(SLjava/lang/String;)V", (short) id,
- json_write_string.get());
- return WIFI_SUCCESS;
-}
-
-wifi_error wifi_nan_stats_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanStatsRequest* msg) {
- ALOGD("wifi_nan_stats_request_mock");
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_nan_config_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanConfigRequest* msg) {
- ALOGD("wifi_nan_config_request_mock");
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_nan_tca_request_mock(transaction_id id,
- wifi_interface_handle iface,
- NanTCARequest* msg) {
- ALOGD("wifi_nan_tca_request_mock");
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_nan_beacon_sdf_payload_request_mock(
- transaction_id id, wifi_interface_handle iface,
- NanBeaconSdfPayloadRequest* msg) {
- ALOGD("wifi_nan_beacon_sdf_payload_request_mock");
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_nan_register_handler_mock(wifi_interface_handle iface,
- NanCallbackHandler handlers) {
- ALOGD("wifi_nan_register_handler_mock");
- mCallbackHandlers = handlers;
- return WIFI_SUCCESS;
-}
-
-wifi_error wifi_nan_get_version_mock(wifi_handle handle, NanVersion* version) {
- ALOGD("wifi_nan_get_version_mock");
- return WIFI_ERROR_UNINITIALIZED;
-}
-
-wifi_error wifi_nan_get_capabilities_mock(transaction_id id,
- wifi_interface_handle iface) {
- JNIHelper helper(mock_mVM);
-
- ALOGD("wifi_nan_get_capabilities_mock");
-
- helper.callMethod(mock_mObj, "getCapabilitiesHalMockNative", "(S)V",
- (short) id);
- return WIFI_SUCCESS;
-}
-
-// Callbacks
-
-extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callNotifyResponse(
- JNIEnv* env, jclass clazz, jshort transaction_id,
- 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_callNotifyResponse: '%s'",
- chars.c_str());
-
- NanResponseMsg msg;
- msg.status = (NanStatusType) jsonR.get_int("status", &error);
- msg.value = jsonR.get_int("value", &error);
- msg.response_type = (NanResponseType) jsonR.get_int("response_type", &error);
- if (msg.response_type == NAN_RESPONSE_PUBLISH) {
- msg.body.publish_response.publish_id = jsonR.get_int(
- "body.publish_response.publish_id", &error);
- } else if (msg.response_type == NAN_RESPONSE_SUBSCRIBE) {
- msg.body.subscribe_response.subscribe_id = jsonR.get_int(
- "body.subscribe_response.subscribe_id", &error);
- } else if (msg.response_type == NAN_GET_CAPABILITIES) {
- msg.body.nan_capabilities.max_concurrent_nan_clusters = jsonR.get_int(
- "body.nan_capabilities.max_concurrent_nan_clusters", &error);
- msg.body.nan_capabilities.max_publishes = jsonR.get_int(
- "body.nan_capabilities.max_publishes", &error);
- msg.body.nan_capabilities.max_subscribes = jsonR.get_int(
- "body.nan_capabilities.max_subscribes", &error);
- msg.body.nan_capabilities.max_service_name_len = jsonR.get_int(
- "body.nan_capabilities.max_service_name_len", &error);
- msg.body.nan_capabilities.max_match_filter_len = jsonR.get_int(
- "body.nan_capabilities.max_match_filter_len", &error);
- msg.body.nan_capabilities.max_total_match_filter_len = jsonR.get_int(
- "body.nan_capabilities.max_total_match_filter_len", &error);
- msg.body.nan_capabilities.max_service_specific_info_len = jsonR.get_int(
- "body.nan_capabilities.max_service_specific_info_len", &error);
- msg.body.nan_capabilities.max_vsa_data_len = jsonR.get_int(
- "body.nan_capabilities.max_vsa_data_len", &error);
- msg.body.nan_capabilities.max_mesh_data_len = jsonR.get_int(
- "body.nan_capabilities.max_mesh_data_len", &error);
- msg.body.nan_capabilities.max_ndi_interfaces = jsonR.get_int(
- "body.nan_capabilities.max_ndi_interfaces", &error);
- msg.body.nan_capabilities.max_ndp_sessions = jsonR.get_int(
- "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);
- }
-
- if (error) {
- ALOGE("Java_com_android_server_wifi_nan_WifiNanHalMock_callNotifyResponse: "
- "error parsing args");
- return;
- }
-
- mCallbackHandlers.NotifyResponse(transaction_id, &msg);
-}
-
-extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callPublishTerminated(
- 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_callPublishTerminated: '%s'",
- chars.c_str());
-
- NanPublishTerminatedInd msg;
- msg.publish_id = jsonR.get_int("publish_id", &error);
- msg.reason = (NanStatusType) jsonR.get_int("reason", &error);
-
- if (error) {
- ALOGE("Java_com_android_server_wifi_nan_WifiNanHalMock_callPublishTerminated: "
- "error parsing args");
- return;
- }
-
- mCallbackHandlers.EventPublishTerminated(&msg);
-}
-
-extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callSubscribeTerminated(
- 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_callSubscribeTerminated: '%s'",
- chars.c_str());
-
- NanSubscribeTerminatedInd msg;
- msg.subscribe_id = jsonR.get_int("subscribe_id", &error);
- msg.reason = (NanStatusType) jsonR.get_int("reason", &error);
-
- if (error) {
- ALOGE("Java_com_android_server_wifi_nan_WifiNanHalMock_callSubscribeTerminated:"
- " error parsing args");
- return;
- }
-
- mCallbackHandlers.EventSubscribeTerminated(&msg);
-}
-
-extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callFollowup(
- 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_callFollowup: '%s'",
- chars.c_str());
-
- NanFollowupInd msg;
- msg.publish_subscribe_id = jsonR.get_int("publish_subscribe_id", &error);
- msg.requestor_instance_id = jsonR.get_int("requestor_instance_id", &error);
- jsonR.get_byte_array("addr", &error, msg.addr, NAN_MAC_ADDR_LEN);
- msg.dw_or_faw = jsonR.get_int("dw_or_faw", &error);
- msg.service_specific_info_len = jsonR.get_int("service_specific_info_len",
- &error);
- jsonR.get_byte_array("service_specific_info", &error,
- msg.service_specific_info,
- NAN_MAX_SERVICE_SPECIFIC_INFO_LEN);
-
- if (error) {
- ALOGE("Java_com_android_server_wifi_nan_WifiNanHalMock_callFollowup: "
- "error parsing args");
- return;
- }
-
- mCallbackHandlers.EventFollowup(&msg);
-}
-
-extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callMatch(
- 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_callMatch: '%s'",
- chars.c_str());
-
- NanMatchInd msg;
- msg.publish_subscribe_id = jsonR.get_int("publish_subscribe_id", &error);
- msg.requestor_instance_id = jsonR.get_int("requestor_instance_id", &error);
- jsonR.get_byte_array("addr", &error, msg.addr, NAN_MAC_ADDR_LEN);
- msg.service_specific_info_len = jsonR.get_int("service_specific_info_len",
- &error);
- jsonR.get_byte_array("service_specific_info", &error,
- msg.service_specific_info,
- NAN_MAX_SERVICE_SPECIFIC_INFO_LEN);
- msg.sdf_match_filter_len = jsonR.get_int("sdf_match_filter_len", &error);
- jsonR.get_byte_array("sdf_match_filter", &error, msg.sdf_match_filter,
- NAN_MAX_MATCH_FILTER_LEN);
- /* a few more fields here - but not used (yet/never?) */
-
- if (error) {
- ALOGE("Java_com_android_server_wifi_nan_WifiNanHalMock_callMatch: "
- "error parsing args");
- return;
- }
-
- mCallbackHandlers.EventMatch(&msg);
-}
-
-extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callDiscEngEvent(
- 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_callDiscEngEvent: '%s'",
- chars.c_str());
-
- NanDiscEngEventInd msg;
- msg.event_type = (NanDiscEngEventType) jsonR.get_int("event_type", &error);
- if (msg.event_type == NAN_EVENT_ID_DISC_MAC_ADDR) {
- jsonR.get_byte_array("data", &error, msg.data.mac_addr.addr,
- NAN_MAC_ADDR_LEN);
- } else {
- jsonR.get_byte_array("data", &error, msg.data.cluster.addr,
- NAN_MAC_ADDR_LEN);
- }
-
- if (error) {
- ALOGE("Java_com_android_server_wifi_nan_WifiNanHalMock_callDiscEngEvent: "
- "error parsing args");
- return;
- }
-
- mCallbackHandlers.EventDiscEngEvent(&msg);
-}
-
-extern "C" void Java_com_android_server_wifi_nan_WifiNanHalMock_callDisabled(
- 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_callDisabled: '%s'",
- chars.c_str());
-
- NanDisabledInd msg;
- msg.reason = (NanStatusType) jsonR.get_int("reason", &error);
-
- if (error) {
- ALOGE("Java_com_android_server_wifi_nan_WifiNanHalMock_callDisabled: "
- "error parsing args");
- return;
- }
-
- mCallbackHandlers.EventDisabled(&msg);
-}
-
-// TODO: Not currently used: add as needed
-//void (*EventUnMatch) (NanUnmatchInd* event);
-//void (*EventTca) (NanTCAInd* event);
-//void (*EventBeaconSdfPayload) (NanBeaconSdfPayloadInd* event);
-
-int init_wifi_nan_hal_func_table_mock(wifi_hal_fn *fn) {
- if (fn == NULL) {
- return -1;
- }
-
- fn->wifi_nan_enable_request = wifi_nan_enable_request_mock;
- fn->wifi_nan_disable_request = wifi_nan_disable_request_mock;
- fn->wifi_nan_publish_request = wifi_nan_publish_request_mock;
- fn->wifi_nan_publish_cancel_request =
- wifi_nan_publish_cancel_request_mock;
- fn->wifi_nan_subscribe_request = wifi_nan_subscribe_request_mock;
- fn->wifi_nan_subscribe_cancel_request =
- wifi_nan_subscribe_cancel_request_mock;
- fn->wifi_nan_transmit_followup_request =
- wifi_nan_transmit_followup_request_mock;
- fn->wifi_nan_stats_request = wifi_nan_stats_request_mock;
- fn->wifi_nan_config_request = wifi_nan_config_request_mock;
- fn->wifi_nan_tca_request = wifi_nan_tca_request_mock;
- fn->wifi_nan_beacon_sdf_payload_request =
- wifi_nan_beacon_sdf_payload_request_mock;
- 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;
-
- return 0;
-}
-
-extern "C" jint Java_com_android_server_wifi_nan_WifiNanHalMock_initNanHalMock(
- JNIEnv* env, jclass clazz) {
- Java_com_android_server_wifi_nan_WifiNanNative_registerNanNatives(env, clazz);
- return init_wifi_nan_hal_func_table_mock(&hal_fn);
-}
-
-}// namespace android
diff --git a/tests/wifitests/runtests.sh b/tests/wifitests/runtests.sh
new file mode 100755
index 0000000..529a535
--- /dev/null
+++ b/tests/wifitests/runtests.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+
+if [[ "$1" == "--help" ]]; then
+ cat <<END
+Usage for $0
+
+ <no-args> run all tests
+ -r print raw results
+ -e class <class-name> run all the tests in <class-name>
+ -e class <class-name>#<method> run just the specified <method>
+
+Example:
+$ $0 -r -e class \\
+ com.android.server.wifi.WifiDiagnosticsTest#startLoggingRegistersLogEventHandler
+Run just the specified test, and show the raw output.
+
+For more options, see https://goo.gl/JxYjIw
+END
+ exit 0
+fi
+
+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 "$@" \
+ -e notAnnotation com.android.server.wifi.DisabledForUpdateToAnyMatcher \
+ 'com.android.server.wifi.test/android.support.test.runner.AndroidJUnitRunner'
diff --git a/tests/wifitests/src/android/net/wifi/FakeKeys.java b/tests/wifitests/src/android/net/wifi/FakeKeys.java
deleted file mode 100644
index 8a1e7b4..0000000
--- a/tests/wifitests/src/android/net/wifi/FakeKeys.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package android.net.wifi;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-
-/**
- * A class containing test certificates.
- */
-public class FakeKeys {
- private static final String CA_CERT0_STRING = "-----BEGIN CERTIFICATE-----\n" +
- "MIIDKDCCAhCgAwIBAgIJAILlFdwzLVurMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
- "BAMTB0VBUCBDQTEwHhcNMTYwMTEyMTE1MDE1WhcNMjYwMTA5MTE1MDE1WjASMRAw\n" +
- "DgYDVQQDEwdFQVAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
- "znAPUz26Msae4ws43czR41/J2QtrSIZUKmVUsVumDbYHrPNvTXKSMXAcewORDQYX\n" +
- "RqvHvpn8CscB1+oGXZvHwxj4zV0WKoK2zeXkau3vcyl3HIKupJfq2TEACefVjj0t\n" +
- "JW+X35PGWp9/H5zIUNVNVjS7Ums84IvKhRB8512PB9UyHagXYVX5GWpAcVpyfrlR\n" +
- "FI9Qdhh+Pbk0uyktdbf/CdfgHOoebrTtwRljM0oDtX+2Cv6j0wBK7hD8pPvf1+uy\n" +
- "GzczigAU/4Kw7eZqydf9B+5RupR+IZipX41xEiIrKRwqi517WWzXcjaG2cNbf451\n" +
- "xpH5PnV3i1tq04jMGQUzFwIDAQABo4GAMH4wHQYDVR0OBBYEFIwX4vs8BiBcScod\n" +
- "5noZHRM8E4+iMEIGA1UdIwQ7MDmAFIwX4vs8BiBcScod5noZHRM8E4+ioRakFDAS\n" +
- "MRAwDgYDVQQDEwdFQVAgQ0ExggkAguUV3DMtW6swDAYDVR0TBAUwAwEB/zALBgNV\n" +
- "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFfQqOTA7Rv7K+luQ7pnas4BYwHE\n" +
- "9GEP/uohv6KOy0TGQFbrRTjFoLVNB9BZ1ymMDZ0/TIwIUc7wi7a8t5mEqYH153wW\n" +
- "aWooiSjyLLhuI4sNrNCOtisdBq2r2MFXt6h0mAQYOPv8R8K7/fgSxGFqzhyNmmVL\n" +
- "1qBJldx34SpwsTALQVPb4hGwJzZfr1PcpEQx6xMnTl8xEWZE3Ms99uaUxbQqIwRu\n" +
- "LgAOkNCmY2m89VhzaHJ1uV85AdM/tD+Ysmlnnjt9LRCejbBipjIGjOXrg1JP+lxV\n" +
- "muM4vH+P/mlmxsPPz0d65b+EGmJZpoLkO/tdNNvCYzjJpTEWpEsO6NMhKYo=\n" +
- "-----END CERTIFICATE-----\n";
- public static final X509Certificate CA_CERT0 = loadCertificate(CA_CERT0_STRING);
-
- private static final String CA_CERT1_STRING = "-----BEGIN CERTIFICATE-----\n" +
- "MIIDKDCCAhCgAwIBAgIJAOM5SzKO2pzCMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
- "BAMTB0VBUCBDQTAwHhcNMTYwMTEyMDAxMDQ3WhcNMjYwMTA5MDAxMDQ3WjASMRAw\n" +
- "DgYDVQQDEwdFQVAgQ0EwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
- "89ug+IEKVQXnJGKg5g4uVHg6J/8iRUxR5k2eH5o03hrJNMfN2D+cBe/wCiZcnWbI\n" +
- "GbGZACWm2nQth2wy9Zgm2LOd3b4ocrHYls3XLq6Qb5Dd7a0JKU7pdGufiNVEkrmF\n" +
- "EB+N64wgwH4COTvCiN4erp5kyJwkfqAl2xLkZo0C464c9XoyQOXbmYD9A8v10wZu\n" +
- "jyNsEo7Nr2USyw+qhjWSbFbEirP77Tvx+7pJQJwdtk1V9Tn73T2dGF2WHYejei9S\n" +
- "mcWpdIUqsu9etYH+zDmtu7I1xlkwiaVsNr2+D+qaCJyOYqrDTKVNK5nmbBPXDWZc\n" +
- "NoDbTOoqquX7xONpq9M6jQIDAQABo4GAMH4wHQYDVR0OBBYEFAZ3A2S4qJZZwuNY\n" +
- "wkJ6mAdc0gVdMEIGA1UdIwQ7MDmAFAZ3A2S4qJZZwuNYwkJ6mAdc0gVdoRakFDAS\n" +
- "MRAwDgYDVQQDEwdFQVAgQ0EwggkA4zlLMo7anMIwDAYDVR0TBAUwAwEB/zALBgNV\n" +
- "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAHmdMwEhtys4d0E+t7owBmoVR+lU\n" +
- "hMCcRtWs8YKX5WIM2kTweT0h/O1xwE1mWmRv/IbDAEb8od4BjAQLhIcolStr2JaO\n" +
- "9ZzyxjOnNzqeErh/1DHDbb/moPpqfeJ8YiEz7nH/YU56Q8iCPO7TsgS0sNNE7PfN\n" +
- "IUsBW0yHRgpQ4OxWmiZG2YZWiECRzAC0ecPzo59N5iH4vLQIMTMYquiDeMPQnn1e\n" +
- "NDGxG8gCtDKIaS6tMg3a28MvWB094pr2ETou8O1C8Ji0Y4hE8QJmSdT7I4+GZjgW\n" +
- "g94DZ5RiL7sdp3vC48CXOmeT61YBIvhGUsE1rPhXqkpqQ3Z3C4TFF0jXZZc=\n" +
- "-----END CERTIFICATE-----\n";
- public static final X509Certificate CA_CERT1 = loadCertificate(CA_CERT1_STRING);
-
-
- private static X509Certificate loadCertificate(String blob) {
- try {
- final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
- InputStream stream = new ByteArrayInputStream(blob.getBytes(StandardCharsets.UTF_8));
-
- return (X509Certificate) certFactory.generateCertificate(stream);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-}
diff --git a/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java
deleted file mode 100644
index 75480b5..0000000
--- a/tests/wifitests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ /dev/null
@@ -1,265 +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 android.net.wifi;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.net.wifi.WifiEnterpriseConfig.Eap;
-import android.net.wifi.WifiEnterpriseConfig.Phase2;
-import android.os.Parcel;
-import android.security.Credentials;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.security.cert.X509Certificate;
-
-
-/**
- * Unit tests for {@link android.net.wifi.WifiEnterpriseConfig}.
- */
-@SmallTest
-public class WifiEnterpriseConfigTest {
- // Maintain a ground truth of the keystore uri prefix which is expected by wpa_supplicant.
- public static final String KEYSTORE_URI = "keystore://";
- public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE;
- public static final String KEYSTORES_URI = "keystores://";
-
- private WifiEnterpriseConfig mEnterpriseConfig;
-
- @Before
- public void setUp() throws Exception {
- mEnterpriseConfig = new WifiEnterpriseConfig();
- }
-
- @Test
- public void testSetGetSingleCaCertificate() {
- X509Certificate cert0 = FakeKeys.CA_CERT0;
- mEnterpriseConfig.setCaCertificate(cert0);
- assertEquals(mEnterpriseConfig.getCaCertificate(), cert0);
- }
-
- @Test
- public void testSetGetMultipleCaCertificates() {
- X509Certificate cert0 = FakeKeys.CA_CERT0;
- X509Certificate cert1 = FakeKeys.CA_CERT1;
- mEnterpriseConfig.setCaCertificates(new X509Certificate[] {cert0, cert1});
- X509Certificate[] result = mEnterpriseConfig.getCaCertificates();
- assertEquals(result.length, 2);
- assertTrue(result[0] == cert0 && result[1] == cert1);
- }
-
- @Test
- public void testSaveSingleCaCertificateAlias() {
- final String alias = "single_alias 0";
- mEnterpriseConfig.setCaCertificateAliases(new String[] {alias});
- assertEquals(getCaCertField(), CA_CERT_PREFIX + alias);
- }
-
- @Test
- public void testLoadSingleCaCertificateAlias() {
- final String alias = "single_alias 1";
- setCaCertField(CA_CERT_PREFIX + alias);
- String[] aliases = mEnterpriseConfig.getCaCertificateAliases();
- assertEquals(aliases.length, 1);
- assertEquals(aliases[0], alias);
- }
-
- @Test
- public void testSaveMultipleCaCertificates() {
- final String alias0 = "single_alias 0";
- final String alias1 = "single_alias 1";
- mEnterpriseConfig.setCaCertificateAliases(new String[] {alias0, alias1});
- assertEquals(getCaCertField(), String.format("%s%s %s",
- KEYSTORES_URI,
- WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias0),
- WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias1)));
- }
-
- @Test
- public void testLoadMultipleCaCertificates() {
- final String alias0 = "single_alias 0";
- final String alias1 = "single_alias 1";
- setCaCertField(String.format("%s%s %s",
- KEYSTORES_URI,
- WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias0),
- WifiEnterpriseConfig.encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + alias1)));
- String[] aliases = mEnterpriseConfig.getCaCertificateAliases();
- assertEquals(aliases.length, 2);
- assertEquals(aliases[0], alias0);
- assertEquals(aliases[1], alias1);
- }
-
- private String getCaCertField() {
- return mEnterpriseConfig.getFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, "");
- }
-
- private void setCaCertField(String value) {
- mEnterpriseConfig.setFieldValue(WifiEnterpriseConfig.CA_CERT_KEY, value);
- }
-
- // Retrieves the value for a specific key supplied to wpa_supplicant.
- private class SupplicantConfigExtractor implements WifiEnterpriseConfig.SupplicantSaver {
- private String mValue = null;
- private String mKey;
-
- SupplicantConfigExtractor(String key) {
- mKey = key;
- }
-
- @Override
- public boolean saveValue(String key, String value) {
- if (key.equals(mKey)) {
- mValue = value;
- }
- return true;
- }
-
- public String getValue() {
- return mValue;
- }
- }
-
- private String getSupplicantEapMethod() {
- SupplicantConfigExtractor entryExtractor = new SupplicantConfigExtractor(
- WifiEnterpriseConfig.EAP_KEY);
- mEnterpriseConfig.saveToSupplicant(entryExtractor);
- return entryExtractor.getValue();
- }
-
- private String getSupplicantPhase2Method() {
- SupplicantConfigExtractor entryExtractor = new SupplicantConfigExtractor(
- WifiEnterpriseConfig.PHASE2_KEY);
- mEnterpriseConfig.saveToSupplicant(entryExtractor);
- return entryExtractor.getValue();
- }
-
- /** Verifies the default value for EAP outer and inner methods */
- @Test
- public void eapInnerDefault() {
- assertEquals(null, getSupplicantEapMethod());
- assertEquals(null, getSupplicantPhase2Method());
- }
-
- /** Verifies that the EAP inner method is reset when we switch to TLS */
- @Test
- public void eapPhase2MethodForTls() {
- // Initially select an EAP method that supports an phase2.
- mEnterpriseConfig.setEapMethod(Eap.PEAP);
- mEnterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
- assertEquals("PEAP", getSupplicantEapMethod());
- assertEquals("\"auth=MSCHAPV2\"", getSupplicantPhase2Method());
-
- // Change the EAP method to another type which supports a phase2.
- mEnterpriseConfig.setEapMethod(Eap.TTLS);
- assertEquals("TTLS", getSupplicantEapMethod());
- assertEquals("\"auth=MSCHAPV2\"", getSupplicantPhase2Method());
-
- // Change the EAP method to TLS which does not support a phase2.
- mEnterpriseConfig.setEapMethod(Eap.TLS);
- assertEquals(null, getSupplicantPhase2Method());
- }
-
- /** Verfies that the EAP inner method is reset when we switch phase2 to NONE */
- @Test
- public void eapPhase2None() {
- // Initially select an EAP method that supports an phase2.
- mEnterpriseConfig.setEapMethod(Eap.PEAP);
- mEnterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
- assertEquals("PEAP", getSupplicantEapMethod());
- assertEquals("\"auth=MSCHAPV2\"", getSupplicantPhase2Method());
-
- // Change the phase2 method to NONE and ensure the value is cleared.
- mEnterpriseConfig.setPhase2Method(Phase2.NONE);
- assertEquals(null, getSupplicantPhase2Method());
- }
-
- /** Verfies that the correct "autheap" parameter is supplied for TTLS/GTC. */
- @Test
- public void peapGtcToTtls() {
- mEnterpriseConfig.setEapMethod(Eap.PEAP);
- mEnterpriseConfig.setPhase2Method(Phase2.GTC);
- assertEquals("PEAP", getSupplicantEapMethod());
- assertEquals("\"auth=GTC\"", getSupplicantPhase2Method());
-
- mEnterpriseConfig.setEapMethod(Eap.TTLS);
- assertEquals("TTLS", getSupplicantEapMethod());
- assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method());
- }
-
- /** Verfies that the correct "auth" parameter is supplied for PEAP/GTC. */
- @Test
- public void ttlsGtcToPeap() {
- mEnterpriseConfig.setEapMethod(Eap.TTLS);
- mEnterpriseConfig.setPhase2Method(Phase2.GTC);
- assertEquals("TTLS", getSupplicantEapMethod());
- assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method());
-
- mEnterpriseConfig.setEapMethod(Eap.PEAP);
- assertEquals("PEAP", getSupplicantEapMethod());
- assertEquals("\"auth=GTC\"", getSupplicantPhase2Method());
- }
-
- /** Verfies that the copy constructor preseves the inner method information. */
- @Test
- public void copyConstructor() {
- WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
- enterpriseConfig.setEapMethod(Eap.TTLS);
- enterpriseConfig.setPhase2Method(Phase2.GTC);
- mEnterpriseConfig = new WifiEnterpriseConfig(enterpriseConfig);
- assertEquals("TTLS", getSupplicantEapMethod());
- assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method());
- }
-
- /** Verfies that parceling a WifiEnterpriseConfig preseves method information. */
- @Test
- public void parcelConstructor() {
- WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
- enterpriseConfig.setEapMethod(Eap.TTLS);
- enterpriseConfig.setPhase2Method(Phase2.GTC);
- Parcel parcel = Parcel.obtain();
- enterpriseConfig.writeToParcel(parcel, 0);
- parcel.setDataPosition(0); // Allow parcel to be read from the beginning.
- mEnterpriseConfig = WifiEnterpriseConfig.CREATOR.createFromParcel(parcel);
- assertEquals("TTLS", getSupplicantEapMethod());
- assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method());
- }
-
- /** Verifies proper operation of the getKeyId() method. */
- @Test
- public void getKeyId() {
- assertEquals("NULL", mEnterpriseConfig.getKeyId(null));
- WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
- enterpriseConfig.setEapMethod(Eap.TTLS);
- enterpriseConfig.setPhase2Method(Phase2.GTC);
- assertEquals("TTLS_GTC", mEnterpriseConfig.getKeyId(enterpriseConfig));
- mEnterpriseConfig.setEapMethod(Eap.PEAP);
- mEnterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
- assertEquals("PEAP_MSCHAPV2", mEnterpriseConfig.getKeyId(enterpriseConfig));
- }
-
- /** Verifies that passwords are not displayed in toString. */
- @Test
- public void passwordNotInToString() {
- String password = "supersecret";
- mEnterpriseConfig.setPassword(password);
- assertFalse(mEnterpriseConfig.toString().contains(password));
- }
-}
diff --git a/tests/wifitests/src/android/net/wifi/WifiScannerTest.java b/tests/wifitests/src/android/net/wifi/WifiScannerTest.java
deleted file mode 100644
index 5034c2a..0000000
--- a/tests/wifitests/src/android/net/wifi/WifiScannerTest.java
+++ /dev/null
@@ -1,111 +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 android.net.wifi;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.validateMockitoUsage;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.wifi.WifiScanner.BssidInfo;
-import android.net.wifi.WifiScanner.BssidListener;
-import android.os.Handler;
-import android.os.Message;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.server.wifi.BidirectionalAsyncChannelServer;
-import com.android.server.wifi.MockLooper;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Unit tests for {@link android.net.wifi.WifiScanner}.
- */
-@SmallTest
-public class WifiScannerTest {
- @Mock
- private Context mContext;
- @Mock
- private IWifiScanner mService;
- @Mock
- private BssidListener mBssidListener;
-
- private WifiScanner mWifiScanner;
- private MockLooper mLooper;
- private Handler mHandler;
-
- /**
- * Setup before tests.
- */
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mLooper = new MockLooper();
- mHandler = mock(Handler.class);
- BidirectionalAsyncChannelServer server = new BidirectionalAsyncChannelServer(
- mContext, mLooper.getLooper(), mHandler);
- when(mService.getMessenger()).thenReturn(server.getMessenger());
- mWifiScanner = new WifiScanner(mContext, mService, mLooper.getLooper());
- mLooper.dispatchAll();
- }
-
- /**
- * Clean up after tests.
- */
- @After
- public void cleanup() {
- validateMockitoUsage();
- }
-
- private void verifySetHotlistMessage(Handler handler) {
- ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
- verify(handler, atLeastOnce()).handleMessage(messageCaptor.capture());
- assertEquals("message.what is not CMD_SET_HOTLIST",
- WifiScanner.CMD_SET_HOTLIST,
- messageCaptor.getValue().what);
- }
-
- /**
- * Test duplicate listeners for bssid tracking.
- */
- @Test
- public void testStartTrackingBssidsDuplicateListeners() throws Exception {
- BssidInfo[] bssids = new BssidInfo[] {
- new BssidInfo()
- };
-
- // First start tracking succeeds.
- mWifiScanner.startTrackingBssids(bssids, -100, mBssidListener);
- mLooper.dispatchAll();
- verifySetHotlistMessage(mHandler);
-
- // Second start tracking should fail.
- mWifiScanner.startTrackingBssids(bssids, -100, mBssidListener);
- mLooper.dispatchAll();
- verify(mBssidListener).onFailure(eq(WifiScanner.REASON_DUPLICATE_REQEUST), anyString());
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/AnqpCacheTest.java b/tests/wifitests/src/com/android/server/wifi/AnqpCacheTest.java
deleted file mode 100644
index 1a96831..0000000
--- a/tests/wifitests/src/com/android/server/wifi/AnqpCacheTest.java
+++ /dev/null
@@ -1,236 +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 static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiSsid;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import com.android.server.wifi.anqp.ANQPElement;
-import com.android.server.wifi.anqp.Constants;
-import com.android.server.wifi.hotspot2.ANQPData;
-import com.android.server.wifi.hotspot2.AnqpCache;
-import com.android.server.wifi.hotspot2.NetworkDetail;
-
-import org.junit.Test;
-import org.mockito.Mock;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * Unit tests for {@link com.android.server.wifi.hotspot2.AnqpCache}.
- */
-@SmallTest
-public class AnqpCacheTest {
-
- private static final String TAG = "AnqpCacheTest";
-
- private static class NetworkDescription {
- ScanDetail[] mScanDetails;
- static int[] sChannels = new int[]{2412, 2437, 2462, 5180, 5220, 5745, 5825};
- static int[] sRSSIs = new int[]{ -50, -80, -60, -80, -55, -90, -75};
-
- NetworkDescription(String ssid, String bssidPrefix) {
- WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(ssid);
- mScanDetails = new ScanDetail[sChannels.length];
- for (int i = 0; i < sChannels.length; i++) {
- String bssid = String.format("%s:%02x", bssidPrefix, i);
- ScanResult.InformationElement[] ie = new ScanResult.InformationElement[1];
- ie[0] = ScanResults.generateSsidIe(ssid);
- List<String> anqpLines = new ArrayList<String>();
- NetworkDetail nd = new NetworkDetail(bssid, ie,
- new ArrayList<String>(), sChannels[i]);
- mScanDetails[i] = new ScanDetail(nd, wifiSsid,
- bssid, "", sRSSIs[i], sChannels[i], Long.MAX_VALUE, ie, anqpLines);
- }
- }
- }
-
- private static final String ATT_SSID = "att_wifi";
- private static final String ATT_BSSID_PREFIX = "aa:44:bb:55:cc";
- private static final String TWC_SSID = "TWCWIFI";
- private static final String TWC_BSSID_PREFIX = "11:aa:22:bb:33";
-
- private static ScanDetail[] getAttWifiNetworkDescription() {
- NetworkDescription network = new NetworkDescription(ATT_SSID, ATT_BSSID_PREFIX);
- return network.mScanDetails;
- }
-
- private static ScanDetail[] getTwcWifiNetworkDescription() {
- NetworkDescription network = new NetworkDescription(TWC_SSID, TWC_BSSID_PREFIX);
- return network.mScanDetails;
- }
-
- private static List<Constants.ANQPElementType> buildQueryList() {
- List<Constants.ANQPElementType> list = Arrays.asList(
- Constants.ANQPElementType.class.getEnumConstants());
- return list;
- }
-
- private static Map<Constants.ANQPElementType, ANQPElement> buildAnqpResult() {
- Map<Constants.ANQPElementType, ANQPElement> elements = new HashMap<>();
- List<Constants.ANQPElementType> list = Arrays.asList(
- Constants.ANQPElementType.class.getEnumConstants());
- for (final Constants.ANQPElementType type : list) {
- ANQPElement element = new ANQPElement(type) {
- @Override
- public Constants.ANQPElementType getID() {
- return super.getID();
- }
- };
- elements.put(type, element);
- }
-
- return elements;
- }
-
- private void advanceTimeAndTrimCache(long howManyMillis) {
- mCurrentTimeMillis += howManyMillis;
- Log.d(TAG, "Time set to " + mCurrentTimeMillis);
- when(mClock.currentTimeMillis()).thenReturn(mCurrentTimeMillis);
- mCache.clear(false, true);
- }
-
- public AnqpCacheTest() {}
-
- private static final long SECOND_MS = 1000;
- private static final long MINUTE_MS = 60 * SECOND_MS;
-
- @Mock Clock mClock;
- long mCurrentTimeMillis = 1000000000;
- AnqpCache mCache;
-
- /** verify that ANQP data is cached per the (rather abstract) spec */
- @Test
- public void basicAddQueryAndExpiry() {
- initMocks(this);
-
- AnqpCache cache = mCache = new AnqpCache(mClock);
- advanceTimeAndTrimCache(0);
-
- List<Constants.ANQPElementType> queryList = buildQueryList();
-
- ScanDetail[] attScanDetails = getAttWifiNetworkDescription();
- ScanDetail[] twcScanDetails = getTwcWifiNetworkDescription();
-
- /* query att network at time 0 */
- for (ScanDetail scanDetail : attScanDetails) {
- cache.initiate(scanDetail.getNetworkDetail(), queryList);
- }
-
- /* verify that no data can be returned */
- for (ScanDetail scanDetail : attScanDetails) {
- ANQPData data = cache.getEntry(scanDetail.getNetworkDetail());
- assertNull(data);
- }
-
- /* update ANQP results after 1 min */
- advanceTimeAndTrimCache(1 * MINUTE_MS);
-
- Map<Constants.ANQPElementType, ANQPElement> anqpResults = buildAnqpResult();
-
- for (ScanDetail scanDetail : attScanDetails) {
- cache.update(scanDetail.getNetworkDetail(), anqpResults);
- }
-
- /* check ANQP results after another 1 min */
- advanceTimeAndTrimCache(1 * MINUTE_MS);
-
- for (ScanDetail scanDetail : attScanDetails) {
- ANQPData data = cache.getEntry(scanDetail.getNetworkDetail());
- assertNotNull(data);
- NetworkDetail nd = data.getNetwork();
- Map<Constants.ANQPElementType, ANQPElement> anqp = data.getANQPElements();
- assertEquals(scanDetail.getBSSIDString(), nd.getBSSIDString());
- assertEquals(anqpResults.size(), anqp.size());
- }
-
- /* query ANQP results for twcwifi after another 10 min */
- advanceTimeAndTrimCache(10 * MINUTE_MS);
-
- for (ScanDetail scanDetail : twcScanDetails) {
- cache.initiate(scanDetail.getNetworkDetail(), queryList);
- }
-
- /* update ANQP results for twcwifi after another 10 min */
- advanceTimeAndTrimCache(1 * MINUTE_MS);
-
- for (ScanDetail scanDetail : twcScanDetails) {
- cache.update(scanDetail.getNetworkDetail(), anqpResults);
- }
-
- /* check all results after 1 minute */
- advanceTimeAndTrimCache(1 * MINUTE_MS);
-
- for (ScanDetail scanDetail : attScanDetails) {
- ANQPData data = cache.getEntry(scanDetail.getNetworkDetail());
- assertNull(data);
- }
-
- for (ScanDetail scanDetail : twcScanDetails) {
- ANQPData data = cache.getEntry(scanDetail.getNetworkDetail());
- assertNotNull(data);
- NetworkDetail nd = data.getNetwork();
- Map<Constants.ANQPElementType, ANQPElement> anqp = data.getANQPElements();
- assertEquals(scanDetail.getBSSIDString(), nd.getBSSIDString());
- assertEquals(anqpResults.size(), anqp.size());
- }
- }
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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/ByteBufferReaderTest.java b/tests/wifitests/src/com/android/server/wifi/ByteBufferReaderTest.java
new file mode 100644
index 0000000..16cd648
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/ByteBufferReaderTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.ByteBufferReader}.
+ */
+@SmallTest
+public class ByteBufferReaderTest {
+ /**
+ * Verify that BufferUnderflowException will be thrown when reading an integer from a buffer
+ * that contained less data than needed.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void readIntegerWithBufferUnderflow() throws Exception {
+ byte[] data = new byte[1];
+ ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
+ ByteBufferReader.readInteger(buffer, buffer.order(), 2);
+ }
+
+ /**
+ * Verify that IllegalArgumentException will be thrown when reading an integer that exceeds
+ * the maximum integer size.
+ *
+ * @throws Exception
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void readIntegerExceedingMaximumLength() throws Exception {
+ int length = ByteBufferReader.MAXIMUM_INTEGER_SIZE + 1;
+ byte[] data = new byte[length];
+ ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
+ ByteBufferReader.readInteger(buffer, buffer.order(), length);
+ }
+
+ /**
+ * Verify that IllegalArgumentException will be thrown when reading an integer with size
+ * less than the minimum.
+ *
+ * @throws Exception
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void readIntegerLessThanMinimumLength() throws Exception {
+ int length = ByteBufferReader.MINIMUM_INTEGER_SIZE - 1;
+ byte[] data = new byte[length];
+ ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
+ ByteBufferReader.readInteger(buffer, buffer.order(), length);
+ }
+
+ /**
+ * Verify that the expected integer value is returned when reading an integer with minimum
+ * integer size.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void readIntegerWithMinimumSize() throws Exception {
+ byte[] data = new byte[] {0x1};
+ ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
+ assertEquals(1, ByteBufferReader.readInteger(buffer, buffer.order(),
+ ByteBufferReader.MINIMUM_INTEGER_SIZE));
+ }
+
+ /**
+ * Verify that the expected integer value is returned when reading an integer with maximum
+ * integer size.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void readIntegerWithMaximumSize() throws Exception {
+ byte[] data = new byte[] {0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11};
+
+ // Little Endian parsing.
+ ByteBuffer leBuffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
+ long leValue = 0x110000000000001fL;
+ assertEquals(leValue, ByteBufferReader.readInteger(leBuffer, leBuffer.order(),
+ ByteBufferReader.MAXIMUM_INTEGER_SIZE));
+
+ // Big Endian parsing.
+ ByteBuffer beBuffer = ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN);
+ long beValue = 0x1f00000000000011L;
+ assertEquals(beValue, ByteBufferReader.readInteger(beBuffer, beBuffer.order(),
+ ByteBufferReader.MAXIMUM_INTEGER_SIZE));
+ }
+
+ /**
+ * Verify that NegativeArraySizeException will be thrown when attempting to read a string with
+ * negative size.
+ *
+ * @throws Exception
+ */
+ @Test(expected = NegativeArraySizeException.class)
+ public void readStringWithNegativeSize() throws Exception {
+ ByteBufferReader.readString(ByteBuffer.wrap(new byte[10]), -1, StandardCharsets.US_ASCII);
+ }
+
+ /**
+ * Verify that an empty String will be returned when reading a string with zero size.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void readStringWithZeroSize() throws Exception {
+ String str = ByteBufferReader.readString(
+ ByteBuffer.wrap(new byte[10]), 0, StandardCharsets.US_ASCII);
+ assertTrue(str.isEmpty());
+ }
+
+ /**
+ * Verify that the expected string value is returned when reading a string from a buffer that
+ * contained a valid string.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void readString() throws Exception {
+ String expectedValue = "Hello World";
+ ByteBuffer buffer = ByteBuffer.wrap(expectedValue.getBytes(StandardCharsets.US_ASCII));
+ String actualValue = ByteBufferReader.readString(
+ buffer, buffer.remaining(), StandardCharsets.US_ASCII);
+ assertEquals(expectedValue, actualValue);
+ }
+
+ /**
+ * Verify that the expected string value is returned when reading a buffer that contained the
+ * size of the string and the string value.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void readStringWithByteLength() throws Exception {
+ String expectedValue = "Hello World";
+ ByteBuffer buffer = ByteBuffer.allocate(expectedValue.length() + 1);
+ buffer.put((byte) expectedValue.length());
+ buffer.put(expectedValue.getBytes(StandardCharsets.US_ASCII));
+ // Rewind the buffer's position to the beginning for reading.
+ buffer.position(0);
+ String actualValue =
+ ByteBufferReader.readStringWithByteLength(buffer, StandardCharsets.US_ASCII);
+ assertEquals(expectedValue, actualValue);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java b/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java
index 328feaf..6827d95 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;
@@ -91,18 +90,15 @@
}
public void switchUser(int newUserId) {
- Set<WifiConfiguration> hiddenConfigurations = new HashSet<>();
- for (WifiConfiguration config : mConfigs.valuesForAllUsers()) {
- if (WifiConfigurationUtil.isVisibleToAnyProfile(config,
- USER_PROFILES.get(mCurrentUserId))
- && !WifiConfigurationUtil.isVisibleToAnyProfile(config,
- USER_PROFILES.get(newUserId))) {
- hiddenConfigurations.add(config);
- }
- }
-
mCurrentUserId = newUserId;
- assertEquals(hiddenConfigurations, new HashSet<>(mConfigs.handleUserSwitch(newUserId)));
+ mConfigs.setNewUser(newUserId);
+ mConfigs.clear();
+ }
+
+ public void addNetworks(List<WifiConfiguration> configs) {
+ for (WifiConfiguration config : configs) {
+ assertNull(mConfigs.put(config));
+ }
}
public void verifyGetters(List<WifiConfiguration> configs) {
@@ -125,8 +121,6 @@
}
assertEquals(config, mConfigs.getForAllUsers(config.networkId));
- assertEquals(config,
- mConfigs.getByConfigKeyIDForAllUsers(config.configKey().hashCode()));
}
// Verify that *ForCurrentUser() methods can be used to access network configurations
@@ -178,16 +172,15 @@
*/
@Test
public void testGettersAndHandleUserSwitch() {
- for (WifiConfiguration config : CONFIGS) {
- assertNull(mConfigs.put(config));
- }
-
+ addNetworks(CONFIGS);
verifyGetters(CONFIGS);
switchUser(10);
+ addNetworks(CONFIGS);
verifyGetters(CONFIGS);
switchUser(11);
+ addNetworks(CONFIGS);
verifyGetters(CONFIGS);
}
diff --git a/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java
new file mode 100644
index 0000000..0db93f5
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/DeletedEphemeralSsidsStoreDataTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import org.junit.Before;
+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.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.DeletedEphemeralSsidsStoreData}.
+ */
+@SmallTest
+public class DeletedEphemeralSsidsStoreDataTest {
+ private static final String TEST_SSID1 = "SSID 1";
+ private static final String TEST_SSID2 = "SSID 2";
+ private static final String TEST_SSID_LIST_XML_STRING =
+ "<set name=\"SSIDList\">\n"
+ + "<string>" + TEST_SSID1 + "</string>\n"
+ + "<string>" + TEST_SSID2 + "</string>\n"
+ + "</set>\n";
+ private static final byte[] TEST_SSID_LIST_XML_BYTES =
+ TEST_SSID_LIST_XML_STRING.getBytes(StandardCharsets.UTF_8);
+ private DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData;
+
+ @Before
+ public void setUp() throws Exception {
+ mDeletedEphemeralSsidsStoreData = new DeletedEphemeralSsidsStoreData();
+ }
+
+ /**
+ * Helper function for serializing configuration data to a XML block.
+ *
+ * @param shared Flag indicating serializing shared or user configurations
+ * @return byte[] of the XML data
+ * @throws Exception
+ */
+ private byte[] serializeData(boolean shared) throws Exception {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ mDeletedEphemeralSsidsStoreData.serializeData(out, shared);
+ out.flush();
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * Helper function for parsing configuration data from a XML block.
+ *
+ * @param data XML data to parse from
+ * @param shared Flag indicating parsing of shared or user configurations
+ * @return SSID list
+ * @throws Exception
+ */
+ private Set<String> deserializeData(byte[] data, boolean shared) throws Exception {
+ final XmlPullParser in = Xml.newPullParser();
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ mDeletedEphemeralSsidsStoreData.deserializeData(in, in.getDepth(), shared);
+ return mDeletedEphemeralSsidsStoreData.getSsidList();
+ }
+
+ /**
+ * Verify that a XmlPullParserException will be thrown when attempting to serialize SSID list
+ * to the share store, since the deleted ephemeral SSID list should never be persist
+ * to the share store.
+ *
+ * @throws Exception
+ */
+ @Test(expected = XmlPullParserException.class)
+ public void serializeShareData() throws Exception {
+ serializeData(true /* shared */);
+ }
+
+ /**
+ * Verify that a XmlPullParserException will be thrown when attempting to parse SSID list
+ * from the share store, since the deleted ephemeral SSID list should never be persist
+ * to the share store.
+ *
+ * @throws Exception
+ */
+ @Test(expected = XmlPullParserException.class)
+ public void deserializeShareData() throws Exception {
+ deserializeData(new byte[0], true /* shared */);
+ }
+
+ /**
+ * Verify that serializing the user store data without any configuration doesn't cause any
+ * crash and no data should be serialized.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void serializeEmptyConfigs() throws Exception {
+ assertEquals(0, serializeData(false /* shared */).length);
+ }
+
+ /**
+ * Verify that parsing an empty data doesn't cause any crash and no configuration should
+ * be deserialized.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void deserializeEmptyData() throws Exception {
+ assertTrue(deserializeData(new byte[0], false /* shared */).isEmpty());
+ }
+
+ /**
+ * Verify that DeletedEphemeralSsidsStoreData does not support share data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void supportShareData() throws Exception {
+ assertFalse(mDeletedEphemeralSsidsStoreData.supportShareData());
+ }
+
+ /**
+ * Verify that user store SSID list is serialized correctly, matches the predefined test
+ * XML data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void serializeSsidList() throws Exception {
+ Set<String> ssidList = new HashSet<>();
+ ssidList.add(TEST_SSID1);
+ ssidList.add(TEST_SSID2);
+ mDeletedEphemeralSsidsStoreData.setSsidList(ssidList);
+ byte[] actualData = serializeData(false /* shared */);
+ assertTrue(Arrays.equals(TEST_SSID_LIST_XML_BYTES, actualData));
+ }
+
+ /**
+ * Verify that user store SSID list is deserialized correctly using the predefined test XML
+ * data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void deserializeSsidList() throws Exception {
+ Set<String> ssidList = new HashSet<>();
+ ssidList.add(TEST_SSID1);
+ ssidList.add(TEST_SSID2);
+ assertEquals(ssidList, deserializeData(TEST_SSID_LIST_XML_BYTES, false /* shared */));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/DisabledForUpdateToAnyMatcher.java b/tests/wifitests/src/com/android/server/wifi/DisabledForUpdateToAnyMatcher.java
new file mode 100644
index 0000000..6f1df47
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/DisabledForUpdateToAnyMatcher.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DisabledForUpdateToAnyMatcher {
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/FakeKeys.java b/tests/wifitests/src/com/android/server/wifi/FakeKeys.java
new file mode 100644
index 0000000..99ce196
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/FakeKeys.java
@@ -0,0 +1,221 @@
+package com.android.server.wifi;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+/**
+ * A class containing test certificates.
+ */
+public class FakeKeys {
+ private static final String CA_CERT0_STRING = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDKDCCAhCgAwIBAgIJAILlFdwzLVurMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
+ "BAMTB0VBUCBDQTEwHhcNMTYwMTEyMTE1MDE1WhcNMjYwMTA5MTE1MDE1WjASMRAw\n" +
+ "DgYDVQQDEwdFQVAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
+ "znAPUz26Msae4ws43czR41/J2QtrSIZUKmVUsVumDbYHrPNvTXKSMXAcewORDQYX\n" +
+ "RqvHvpn8CscB1+oGXZvHwxj4zV0WKoK2zeXkau3vcyl3HIKupJfq2TEACefVjj0t\n" +
+ "JW+X35PGWp9/H5zIUNVNVjS7Ums84IvKhRB8512PB9UyHagXYVX5GWpAcVpyfrlR\n" +
+ "FI9Qdhh+Pbk0uyktdbf/CdfgHOoebrTtwRljM0oDtX+2Cv6j0wBK7hD8pPvf1+uy\n" +
+ "GzczigAU/4Kw7eZqydf9B+5RupR+IZipX41xEiIrKRwqi517WWzXcjaG2cNbf451\n" +
+ "xpH5PnV3i1tq04jMGQUzFwIDAQABo4GAMH4wHQYDVR0OBBYEFIwX4vs8BiBcScod\n" +
+ "5noZHRM8E4+iMEIGA1UdIwQ7MDmAFIwX4vs8BiBcScod5noZHRM8E4+ioRakFDAS\n" +
+ "MRAwDgYDVQQDEwdFQVAgQ0ExggkAguUV3DMtW6swDAYDVR0TBAUwAwEB/zALBgNV\n" +
+ "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFfQqOTA7Rv7K+luQ7pnas4BYwHE\n" +
+ "9GEP/uohv6KOy0TGQFbrRTjFoLVNB9BZ1ymMDZ0/TIwIUc7wi7a8t5mEqYH153wW\n" +
+ "aWooiSjyLLhuI4sNrNCOtisdBq2r2MFXt6h0mAQYOPv8R8K7/fgSxGFqzhyNmmVL\n" +
+ "1qBJldx34SpwsTALQVPb4hGwJzZfr1PcpEQx6xMnTl8xEWZE3Ms99uaUxbQqIwRu\n" +
+ "LgAOkNCmY2m89VhzaHJ1uV85AdM/tD+Ysmlnnjt9LRCejbBipjIGjOXrg1JP+lxV\n" +
+ "muM4vH+P/mlmxsPPz0d65b+EGmJZpoLkO/tdNNvCYzjJpTEWpEsO6NMhKYo=\n" +
+ "-----END CERTIFICATE-----\n";
+ public static final X509Certificate CA_CERT0 = loadCertificate(CA_CERT0_STRING);
+
+ private static final String CA_CERT1_STRING = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDKDCCAhCgAwIBAgIJAOM5SzKO2pzCMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
+ "BAMTB0VBUCBDQTAwHhcNMTYwMTEyMDAxMDQ3WhcNMjYwMTA5MDAxMDQ3WjASMRAw\n" +
+ "DgYDVQQDEwdFQVAgQ0EwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
+ "89ug+IEKVQXnJGKg5g4uVHg6J/8iRUxR5k2eH5o03hrJNMfN2D+cBe/wCiZcnWbI\n" +
+ "GbGZACWm2nQth2wy9Zgm2LOd3b4ocrHYls3XLq6Qb5Dd7a0JKU7pdGufiNVEkrmF\n" +
+ "EB+N64wgwH4COTvCiN4erp5kyJwkfqAl2xLkZo0C464c9XoyQOXbmYD9A8v10wZu\n" +
+ "jyNsEo7Nr2USyw+qhjWSbFbEirP77Tvx+7pJQJwdtk1V9Tn73T2dGF2WHYejei9S\n" +
+ "mcWpdIUqsu9etYH+zDmtu7I1xlkwiaVsNr2+D+qaCJyOYqrDTKVNK5nmbBPXDWZc\n" +
+ "NoDbTOoqquX7xONpq9M6jQIDAQABo4GAMH4wHQYDVR0OBBYEFAZ3A2S4qJZZwuNY\n" +
+ "wkJ6mAdc0gVdMEIGA1UdIwQ7MDmAFAZ3A2S4qJZZwuNYwkJ6mAdc0gVdoRakFDAS\n" +
+ "MRAwDgYDVQQDEwdFQVAgQ0EwggkA4zlLMo7anMIwDAYDVR0TBAUwAwEB/zALBgNV\n" +
+ "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAHmdMwEhtys4d0E+t7owBmoVR+lU\n" +
+ "hMCcRtWs8YKX5WIM2kTweT0h/O1xwE1mWmRv/IbDAEb8od4BjAQLhIcolStr2JaO\n" +
+ "9ZzyxjOnNzqeErh/1DHDbb/moPpqfeJ8YiEz7nH/YU56Q8iCPO7TsgS0sNNE7PfN\n" +
+ "IUsBW0yHRgpQ4OxWmiZG2YZWiECRzAC0ecPzo59N5iH4vLQIMTMYquiDeMPQnn1e\n" +
+ "NDGxG8gCtDKIaS6tMg3a28MvWB094pr2ETou8O1C8Ji0Y4hE8QJmSdT7I4+GZjgW\n" +
+ "g94DZ5RiL7sdp3vC48CXOmeT61YBIvhGUsE1rPhXqkpqQ3Z3C4TFF0jXZZc=\n" +
+ "-----END CERTIFICATE-----\n";
+ public static final X509Certificate CA_CERT1 = loadCertificate(CA_CERT1_STRING);
+
+ private static final String CLIENT_CERT_STR = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIE/DCCAuQCAQEwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxCzAJBgNV\n" +
+ "BAgMAkNBMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdUZXN0aW5n\n" +
+ "MB4XDTE2MDkzMDIwNTQyOFoXDTE3MDkzMDIwNTQyOFowRDELMAkGA1UEBhMCVVMx\n" +
+ "CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdU\n" +
+ "ZXN0aW5nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnpmcbuaeHfnJ\n" +
+ "k+2QNvxmdVFTawyFMNk0USCq5sexscwmxbewG/Rb8YnixwJWS44v2XkSujB67z5C\n" +
+ "s2qudFEhRXKdEuC6idbAuA97KjipHh0AAniWMsyv61fvbgsUC0b0canx3LiDq81p\n" +
+ "y28NNGmAvoazLZUZ4AhBRiwYZY6FKk723gmZoGbEIeG7J1dlXPusc1662rIjz4eU\n" +
+ "zlmmlvqyHfNqnNk8L14Vug6Xh+lOEGN85xhu1YHAEKGrS89kZxs5rum/cZU8KH2V\n" +
+ "v6eKnY03kxjiVLQtnLpm/7VUEoCMGHyruRj+p3my4+DgqMsmsH52RZCBsjyGlpbU\n" +
+ "NOwOTIX6xh+Rqloduz4AnrMYYIiIw2s8g+2zJM7VbcVKx0fGS26BKdrxgrXWfmNE\n" +
+ "nR0/REQ5AxDGw0jfTUvtdTkXAf+K4MDjcNLEZ+MA4rHfAfQWZtUR5BkHCQYxNpJk\n" +
+ "pA0gyk+BpKdC4WdzI14NSWsu5sRCmBCFqH6BTOSEq/V1cNorBxNwLSSTwFFqUDqx\n" +
+ "Y5nQLXygkJf9WHZWtSKeSjtOYgilz7UKzC2s3CsjmIyGFe+SwpuHJnuE4Uc8Z5Cb\n" +
+ "bjNGHPzqL6XnmzZHJp7RF8kBdKdjGC7dCUltzOfICZeKlzOOq+Kw42T/nXjuXvpb\n" +
+ "nkXNxg741Nwd6RecykXJbseFwm3EYxkCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEA\n" +
+ "Ga1mGwI9aXkL2fTPXO9YkAPzoGeX8aeuVYSQaSkNq+5vnogYCyAt3YDHjRG+ewTT\n" +
+ "WbnPA991xRAPac+biJeXWmwvgGj0YuT7e79phAiGkTTnbAjFHGfYnBy/tI/v7btO\n" +
+ "hRNElA5yTJ1m2fVbBEKXzMR83jrT9iyI+YLRN86zUZIaC86xxSbqnrdWN2jOK6MX\n" +
+ "dS8Arp9tPQjC/4gW+2Ilxv68jiYh+5auWHQZVjppWVY//iu4mAbkq1pTwQEhZ8F8\n" +
+ "Zrmh9DHh60hLFcfSuhIAwf/NMzppwdkjy1ruKVrpijhGKGp4OWu8nvOUgHSzxc7F\n" +
+ "PwpVZ5N2Ku4L8MLO6BG2VasRJK7l17TzDXlfLZHJjkuryOFxVaQKt8ZNFgTOaCXS\n" +
+ "E+gpTLksKU7riYckoiP4+H1sn9qcis0e8s4o/uf1UVc8GSdDw61ReGM5oZEDm1u8\n" +
+ "H9x20QU6igLqzyBpqvCKv7JNgU1uB2PAODHH78zJiUfnKd1y+o+J1iWzaGj3EFji\n" +
+ "T8AXksbTP733FeFXfggXju2dyBH+Z1S5BBTEOd1brWgXlHSAZGm97MKZ94r6/tkX\n" +
+ "qfv3fCos0DKz0oV7qBxYS8wiYhzrRVxG6ITAoH8uuUVVQaZF+G4nJ2jEqNbfuKyX\n" +
+ "ATQsVNjNNlDA0J33GobPMjT326wa4YAWMx8PI5PJZ3g=\n" +
+ "-----END CERTIFICATE-----\n";
+ public static final X509Certificate CLIENT_CERT = loadCertificate(CLIENT_CERT_STR);
+
+ private static final byte[] FAKE_RSA_KEY_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
+ (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+ (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+ (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82,
+ (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
+ (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
+ (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
+ (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
+ (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
+ (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
+ (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
+ (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
+ (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
+ (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
+ (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
+ (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
+ (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
+ (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
+ (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
+ (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
+ (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
+ (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
+ (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
+ (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
+ (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
+ (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
+ (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
+ (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
+ (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
+ (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
+ (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
+ (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
+ (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
+ (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
+ (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
+ (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
+ (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
+ (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
+ (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
+ (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
+ (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
+ (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
+ (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
+ (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
+ (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
+ (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
+ (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
+ (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
+ (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
+ (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
+ (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
+ (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
+ (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
+ (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
+ (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
+ (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
+ (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
+ (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
+ (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
+ (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
+ (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
+ (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
+ (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
+ (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
+ (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
+ (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
+ (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
+ (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
+ (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
+ (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
+ (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
+ (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
+ (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
+ (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
+ (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
+ (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
+ (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
+ (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
+ (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
+ (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
+ (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
+ (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
+ (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
+ (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
+ (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
+ (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
+ (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
+ (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
+ (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
+ (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
+ (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
+ (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
+ (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
+ (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
+ (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
+ (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
+ (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
+ (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
+ (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
+ (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
+ (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
+ (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
+ (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
+ (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
+ (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
+ };
+ public static final PrivateKey RSA_KEY1 = loadPrivateRSAKey(FAKE_RSA_KEY_1);
+
+ private static X509Certificate loadCertificate(String blob) {
+ try {
+ final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ InputStream stream = new ByteArrayInputStream(blob.getBytes(StandardCharsets.UTF_8));
+
+ return (X509Certificate) certFactory.generateCertificate(stream);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private static PrivateKey loadPrivateRSAKey(byte[] fakeKey) {
+ try {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ return kf.generatePrivate(new PKCS8EncodedKeySpec(fakeKey));
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
+ return null;
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
new file mode 100644
index 0000000..3e203a6
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java
@@ -0,0 +1,1635 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static com.android.server.wifi.HalDeviceManager.START_HAL_RETRY_TIMES;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+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.when;
+
+import android.app.test.MockAnswerUtil;
+import android.hardware.wifi.V1_0.IWifi;
+import android.hardware.wifi.V1_0.IWifiApIface;
+import android.hardware.wifi.V1_0.IWifiChip;
+import android.hardware.wifi.V1_0.IWifiChipEventCallback;
+import android.hardware.wifi.V1_0.IWifiEventCallback;
+import android.hardware.wifi.V1_0.IWifiIface;
+import android.hardware.wifi.V1_0.IWifiNanIface;
+import android.hardware.wifi.V1_0.IWifiP2pIface;
+import android.hardware.wifi.V1_0.IWifiStaIface;
+import android.hardware.wifi.V1_0.IfaceType;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.IHwBinder;
+import android.os.test.TestLooper;
+import android.util.Log;
+
+import org.hamcrest.core.IsNull;
+import org.junit.After;
+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.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Unit test harness for HalDeviceManagerTest.
+ */
+public class HalDeviceManagerTest {
+ private HalDeviceManager mDut;
+ @Mock IServiceManager mServiceManagerMock;
+ @Mock IWifi mWifiMock;
+ @Mock HalDeviceManager.ManagerStatusListener mManagerStatusListenerMock;
+ private TestLooper mTestLooper;
+ private ArgumentCaptor<IHwBinder.DeathRecipient> mDeathRecipientCaptor =
+ ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
+ private ArgumentCaptor<IServiceNotification.Stub> mServiceNotificationCaptor =
+ ArgumentCaptor.forClass(IServiceNotification.Stub.class);
+ private ArgumentCaptor<IWifiEventCallback> mWifiEventCallbackCaptor = ArgumentCaptor.forClass(
+ IWifiEventCallback.class);
+ private InOrder mInOrder;
+ @Rule public ErrorCollector collector = new ErrorCollector();
+ private WifiStatus mStatusOk;
+ private WifiStatus mStatusFail;
+
+ private class HalDeviceManagerSpy extends HalDeviceManager {
+ @Override
+ protected IWifi getWifiServiceMockable() {
+ return mWifiMock;
+ }
+
+ @Override
+ protected IServiceManager getServiceManagerMockable() {
+ return mServiceManagerMock;
+ }
+ }
+
+ @Before
+ public void before() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mTestLooper = new TestLooper();
+
+ // initialize dummy status objects
+ mStatusOk = getStatus(WifiStatusCode.SUCCESS);
+ mStatusFail = getStatus(WifiStatusCode.ERROR_UNKNOWN);
+
+ when(mServiceManagerMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong())).thenReturn(true);
+ when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
+ any(IServiceNotification.Stub.class))).thenReturn(true);
+ when(mWifiMock.linkToDeath(any(IHwBinder.DeathRecipient.class), anyLong())).thenReturn(
+ true);
+ when(mWifiMock.registerEventCallback(any(IWifiEventCallback.class))).thenReturn(mStatusOk);
+ when(mWifiMock.start()).thenReturn(mStatusOk);
+ when(mWifiMock.stop()).thenReturn(mStatusOk);
+
+ mDut = new HalDeviceManagerSpy();
+ }
+
+ /**
+ * Print out the dump of the device manager after each test. Not used in test validation
+ * (internal state) - but can help in debugging failed tests.
+ */
+ @After
+ public void after() throws Exception {
+ dumpDut("after: ");
+ }
+
+ /**
+ * Test basic startup flow:
+ * - IServiceManager registrations
+ * - IWifi registrations
+ * - IWifi startup delayed
+ * - Start Wi-Fi -> onStart
+ * - Stop Wi-Fi -> onStop
+ */
+ @Test
+ public void testStartStopFlow() throws Exception {
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ // act: stop Wi-Fi
+ mDut.stop();
+ mTestLooper.dispatchAll();
+
+ // verify: onStop called
+ mInOrder.verify(mWifiMock).stop();
+ mInOrder.verify(mManagerStatusListenerMock).onStatusChanged();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock);
+ }
+
+ /**
+ * Validate that multiple callback registrations are called and that duplicate ones are
+ * only called once.
+ */
+ @Test
+ public void testMultipleCallbackRegistrations() throws Exception {
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+
+ // register another 2 callbacks - one of them twice
+ HalDeviceManager.ManagerStatusListener callback1 = mock(
+ HalDeviceManager.ManagerStatusListener.class);
+ HalDeviceManager.ManagerStatusListener callback2 = mock(
+ HalDeviceManager.ManagerStatusListener.class);
+ mDut.registerStatusListener(callback2, mTestLooper.getLooper());
+ mDut.registerStatusListener(callback1, mTestLooper.getLooper());
+ mDut.registerStatusListener(callback2, mTestLooper.getLooper());
+
+ // startup
+ executeAndValidateStartupSequence();
+
+ // verify
+ verify(callback1).onStatusChanged();
+ verify(callback2).onStatusChanged();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, callback1, callback2);
+ }
+
+ /**
+ * Validate IWifi death listener and registration flow.
+ */
+ @Test
+ public void testWifiDeathAndRegistration() throws Exception {
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ // act: IWifi service death
+ mDeathRecipientCaptor.getValue().serviceDied(0);
+ mTestLooper.dispatchAll();
+
+ // verify: getting onStop
+ mInOrder.verify(mManagerStatusListenerMock).onStatusChanged();
+
+ // act: service startup
+ mServiceNotificationCaptor.getValue().onRegistration(IWifi.kInterfaceName, "", false);
+
+ // verify: initialization of IWifi
+ mInOrder.verify(mWifiMock).linkToDeath(mDeathRecipientCaptor.capture(), anyLong());
+ mInOrder.verify(mWifiMock).registerEventCallback(mWifiEventCallbackCaptor.capture());
+
+ // act: start
+ collector.checkThat(mDut.start(), equalTo(true));
+ mWifiEventCallbackCaptor.getValue().onStart();
+ mTestLooper.dispatchAll();
+
+ // verify: service and callback calls
+ mInOrder.verify(mWifiMock).start();
+ mInOrder.verify(mManagerStatusListenerMock, times(3)).onStatusChanged();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock);
+ }
+
+ /**
+ * Validate IWifi onFailure causes notification
+ */
+ @Test
+ public void testWifiFail() throws Exception {
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ // act: IWifi failure
+ mWifiEventCallbackCaptor.getValue().onFailure(mStatusFail);
+ mTestLooper.dispatchAll();
+
+ // verify: getting onStop
+ mInOrder.verify(mManagerStatusListenerMock).onStatusChanged();
+
+ // act: start again
+ collector.checkThat(mDut.start(), equalTo(true));
+ mWifiEventCallbackCaptor.getValue().onStart();
+ mTestLooper.dispatchAll();
+
+ // verify: service and callback calls
+ mInOrder.verify(mWifiMock).start();
+ mInOrder.verify(mManagerStatusListenerMock).onStatusChanged();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock);
+ }
+
+ /**
+ * Validate creation of STA interface from blank start-up. The remove interface.
+ */
+ @Test
+ public void testCreateStaInterfaceNoInitMode() throws Exception {
+ final String name = "sta0";
+
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener idl = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener iafrl = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ IWifiStaIface iface = (IWifiStaIface) validateInterfaceSequence(chipMock,
+ false, // chipModeValid
+ -1000, // chipModeId (only used if chipModeValid is true)
+ IfaceType.STA, // ifaceTypeToCreate
+ name, // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ idl, // destroyedListener
+ iafrl // availableListener
+ );
+ collector.checkThat("allocated interface", iface, IsNull.notNullValue());
+
+ // act: remove interface
+ mDut.removeIface(iface);
+ mTestLooper.dispatchAll();
+
+ // verify: callback triggered
+ mInOrder.verify(chipMock.chip).removeStaIface(name);
+ verify(idl).onDestroyed();
+ verify(iafrl).onAvailableForRequest();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, idl, iafrl);
+ }
+
+ /**
+ * Validate creation of AP interface from blank start-up. The remove interface.
+ */
+ @Test
+ public void testCreateApInterfaceNoInitMode() throws Exception {
+ final String name = "ap0";
+
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener idl = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener iafrl = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ IWifiApIface iface = (IWifiApIface) validateInterfaceSequence(chipMock,
+ false, // chipModeValid
+ -1000, // chipModeId (only used if chipModeValid is true)
+ IfaceType.AP, // ifaceTypeToCreate
+ name, // ifaceName
+ BaselineChip.AP_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ idl, // destroyedListener
+ iafrl // availableListener
+ );
+ collector.checkThat("allocated interface", iface, IsNull.notNullValue());
+
+ // act: remove interface
+ mDut.removeIface(iface);
+ mTestLooper.dispatchAll();
+
+ // verify: callback triggered
+ mInOrder.verify(chipMock.chip).removeApIface(name);
+ verify(idl).onDestroyed();
+ verify(iafrl).onAvailableForRequest();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, idl, iafrl);
+ }
+
+ /**
+ * Validate creation of P2P interface from blank start-up. The remove interface.
+ */
+ @Test
+ public void testCreateP2pInterfaceNoInitMode() throws Exception {
+ final String name = "p2p0";
+
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener idl = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener iafrl = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ IWifiP2pIface iface = (IWifiP2pIface) validateInterfaceSequence(chipMock,
+ false, // chipModeValid
+ -1000, // chipModeId (only used if chipModeValid is true)
+ IfaceType.P2P, // ifaceTypeToCreate
+ name, // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ idl, // destroyedListener
+ iafrl // availableListener
+ );
+ collector.checkThat("allocated interface", iface, IsNull.notNullValue());
+
+ // act: remove interface
+ mDut.removeIface(iface);
+ mTestLooper.dispatchAll();
+
+ // verify: callback triggered
+ mInOrder.verify(chipMock.chip).removeP2pIface(name);
+ verify(idl).onDestroyed();
+ verify(iafrl).onAvailableForRequest();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, idl, iafrl);
+ }
+
+ /**
+ * Validate creation of NAN interface from blank start-up. The remove interface.
+ */
+ @Test
+ public void testCreateNanInterfaceNoInitMode() throws Exception {
+ final String name = "nan0";
+
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener idl = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener iafrl = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ IWifiNanIface iface = (IWifiNanIface) validateInterfaceSequence(chipMock,
+ false, // chipModeValid
+ -1000, // chipModeId (only used if chipModeValid is true)
+ IfaceType.NAN, // ifaceTypeToCreate
+ name, // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ idl, // destroyedListener
+ iafrl // availableListener
+ );
+ collector.checkThat("allocated interface", iface, IsNull.notNullValue());
+
+ // act: remove interface
+ mDut.removeIface(iface);
+ mTestLooper.dispatchAll();
+
+ // verify: callback triggered
+ mInOrder.verify(chipMock.chip).removeNanIface(name);
+ verify(idl).onDestroyed();
+ verify(iafrl).onAvailableForRequest();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, idl, iafrl);
+ }
+
+ /**
+ * Validate creation of AP interface when in STA mode - but with no interface created. Expect
+ * a change in chip mode.
+ */
+ @Test
+ public void testCreateApWithStaModeUp() throws Exception {
+ final String name = "ap0";
+
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener idl = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener iafrl = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ IWifiApIface iface = (IWifiApIface) validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.STA_CHIP_MODE_ID, // chipModeId
+ IfaceType.AP, // ifaceTypeToCreate
+ name, // ifaceName
+ BaselineChip.AP_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ idl, // destroyedListener
+ iafrl // availableListener
+ );
+ collector.checkThat("allocated interface", iface, IsNull.notNullValue());
+
+ // act: stop Wi-Fi
+ mDut.stop();
+ mTestLooper.dispatchAll();
+
+ // verify: callback triggered
+ verify(idl).onDestroyed();
+ verify(mManagerStatusListenerMock, times(2)).onStatusChanged();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, idl, iafrl);
+ }
+
+ /**
+ * Validate creation of AP interface when in AP mode - but with no interface created. Expect
+ * no change in chip mode.
+ */
+ @Test
+ public void testCreateApWithApModeUp() throws Exception {
+ final String name = "ap0";
+
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener idl = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener iafrl = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ IWifiApIface iface = (IWifiApIface) validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.AP_CHIP_MODE_ID, // chipModeId
+ IfaceType.AP, // ifaceTypeToCreate
+ name, // ifaceName
+ BaselineChip.AP_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ idl, // destroyedListener
+ iafrl // availableListener
+ );
+ collector.checkThat("allocated interface", iface, IsNull.notNullValue());
+
+ // act: stop Wi-Fi
+ mDut.stop();
+ mTestLooper.dispatchAll();
+
+ // verify: callback triggered
+ verify(idl).onDestroyed();
+ verify(mManagerStatusListenerMock, times(2)).onStatusChanged();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, idl, iafrl);
+ }
+
+ /**
+ * Validate AP up/down creation of AP interface when a STA already created. Expect:
+ * - STA created
+ * - P2P created
+ * - When AP requested:
+ * - STA & P2P torn down
+ * - AP created
+ * - P2P creation refused
+ * - Request STA: will tear down AP
+ * - When AP destroyed:
+ * - Get p2p available listener callback
+ * - Can create P2P when requested
+ * - Create P2P
+ * - Request NAN: will get refused
+ * - Tear down P2P:
+ * - should get nan available listener callback
+ * - Can create NAN when requested
+ */
+ @Test
+ public void testCreateSameAndDiffPriorities() throws Exception {
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener staDestroyedListener = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener staAvailListener = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ HalDeviceManager.InterfaceDestroyedListener staDestroyedListener2 = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+
+ HalDeviceManager.InterfaceDestroyedListener apDestroyedListener = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener apAvailListener = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ HalDeviceManager.InterfaceDestroyedListener p2pDestroyedListener = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener p2pAvailListener = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ HalDeviceManager.InterfaceDestroyedListener p2pDestroyedListener2 = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+
+ HalDeviceManager.InterfaceDestroyedListener nanDestroyedListener = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener nanAvailListener = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ // Request STA
+ IWifiIface staIface = validateInterfaceSequence(chipMock,
+ false, // chipModeValid
+ -1000, // chipModeId (only used if chipModeValid is true)
+ IfaceType.STA, // ifaceTypeToCreate
+ "sta0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ staDestroyedListener, // destroyedListener
+ staAvailListener // availableListener
+ );
+ collector.checkThat("allocated STA interface", staIface, IsNull.notNullValue());
+
+ // register additional InterfaceDestroyedListeners - including a duplicate (verify that
+ // only called once!)
+ mDut.registerDestroyedListener(staIface, staDestroyedListener2, mTestLooper.getLooper());
+ mDut.registerDestroyedListener(staIface, staDestroyedListener, mTestLooper.getLooper());
+
+ // Request P2P
+ IWifiIface p2pIface = validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.STA_CHIP_MODE_ID, // chipModeId
+ IfaceType.P2P, // ifaceTypeToCreate
+ "p2p0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ p2pDestroyedListener, // destroyedListener
+ p2pAvailListener // availableListener
+ );
+ collector.checkThat("allocated P2P interface", p2pIface, IsNull.notNullValue());
+
+ // Request AP
+ IWifiIface apIface = validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.STA_CHIP_MODE_ID, // chipModeId
+ IfaceType.AP, // ifaceTypeToCreate
+ "ap0", // ifaceName
+ BaselineChip.AP_CHIP_MODE_ID, // finalChipMode
+ new IWifiIface[]{staIface, p2pIface}, // tearDownList
+ apDestroyedListener, // destroyedListener
+ apAvailListener, // availableListener
+ // destroyedInterfacesDestroyedListeners...
+ staDestroyedListener, staDestroyedListener2, p2pDestroyedListener
+ );
+ collector.checkThat("allocated AP interface", apIface, IsNull.notNullValue());
+
+ // Request P2P: expect failure
+ p2pIface = mDut.createP2pIface(p2pDestroyedListener, mTestLooper.getLooper());
+ collector.checkThat("P2P can't be created", p2pIface, IsNull.nullValue());
+
+ // Request STA: expect success
+ staIface = validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.AP_CHIP_MODE_ID, // chipModeId
+ IfaceType.STA, // ifaceTypeToCreate
+ "sta0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ staDestroyedListener, // destroyedListener
+ staAvailListener, // availableListener
+ apDestroyedListener // destroyedInterfacesDestroyedListeners...
+ );
+ collector.checkThat("allocated STA interface", staIface, IsNull.notNullValue());
+
+ mTestLooper.dispatchAll();
+ verify(apDestroyedListener).onDestroyed();
+
+ // Request P2P: expect success now
+ p2pIface = validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.STA_CHIP_MODE_ID, // chipModeId
+ IfaceType.P2P, // ifaceTypeToCreate
+ "p2p0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ p2pDestroyedListener2, // destroyedListener
+ p2pAvailListener // availableListener
+ );
+
+ // Request NAN: should fail
+ IWifiIface nanIface = mDut.createNanIface(nanDestroyedListener, mTestLooper.getLooper());
+ mDut.registerInterfaceAvailableForRequestListener(IfaceType.NAN, nanAvailListener,
+ mTestLooper.getLooper());
+ collector.checkThat("NAN can't be created", nanIface, IsNull.nullValue());
+
+ // Tear down P2P
+ mDut.removeIface(p2pIface);
+ mTestLooper.dispatchAll();
+
+ verify(chipMock.chip, times(2)).removeP2pIface("p2p0");
+ verify(p2pDestroyedListener2).onDestroyed();
+
+ // Should now be able to request and get NAN
+ nanIface = validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.STA_CHIP_MODE_ID, // chipModeId
+ IfaceType.NAN, // ifaceTypeToCreate
+ "nan0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ nanDestroyedListener, // destroyedListener
+ nanAvailListener // availableListener
+ );
+ collector.checkThat("allocated NAN interface", nanIface, IsNull.notNullValue());
+
+ // available callback verification
+ verify(staAvailListener).onAvailableForRequest();
+ verify(apAvailListener, times(4)).onAvailableForRequest();
+ verify(p2pAvailListener, times(3)).onAvailableForRequest();
+ verify(nanAvailListener).onAvailableForRequest();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener, staAvailListener,
+ staDestroyedListener2, apDestroyedListener, apAvailListener, p2pDestroyedListener,
+ nanDestroyedListener, nanAvailListener, p2pDestroyedListener2);
+ }
+
+ /**
+ * Validate P2P and NAN interactions. Expect:
+ * - STA created
+ * - NAN created
+ * - When P2P requested:
+ * - NAN torn down
+ * - P2P created
+ * - NAN creation refused
+ * - When P2P destroyed:
+ * - get nan available listener
+ * - Can create NAN when requested
+ */
+ @Test
+ public void testP2pAndNanInteractions() throws Exception {
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener staDestroyedListener = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener staAvailListener = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ HalDeviceManager.InterfaceDestroyedListener nanDestroyedListener = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener nanAvailListener = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ HalDeviceManager.InterfaceDestroyedListener p2pDestroyedListener = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener p2pAvailListener = null;
+
+ // Request STA
+ IWifiIface staIface = validateInterfaceSequence(chipMock,
+ false, // chipModeValid
+ -1000, // chipModeId (only used if chipModeValid is true)
+ IfaceType.STA, // ifaceTypeToCreate
+ "sta0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ staDestroyedListener, // destroyedListener
+ staAvailListener // availableListener
+ );
+
+ // Request NAN
+ IWifiIface nanIface = validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.STA_CHIP_MODE_ID, // chipModeId
+ IfaceType.NAN, // ifaceTypeToCreate
+ "nan0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ nanDestroyedListener, // destroyedListener
+ nanAvailListener // availableListener
+ );
+
+ // Request P2P
+ IWifiIface p2pIface = validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.STA_CHIP_MODE_ID, // chipModeId
+ IfaceType.P2P, // ifaceTypeToCreate
+ "p2p0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ new IWifiIface[]{nanIface}, // tearDownList
+ p2pDestroyedListener, // destroyedListener
+ p2pAvailListener, // availableListener
+ nanDestroyedListener // destroyedInterfacesDestroyedListeners...
+ );
+
+ // Request NAN: expect failure
+ nanIface = mDut.createNanIface(nanDestroyedListener, mTestLooper.getLooper());
+ mDut.registerInterfaceAvailableForRequestListener(IfaceType.NAN, nanAvailListener,
+ mTestLooper.getLooper());
+ collector.checkThat("NAN can't be created", nanIface, IsNull.nullValue());
+
+ // Destroy P2P interface
+ boolean status = mDut.removeIface(p2pIface);
+ mInOrder.verify(chipMock.chip).removeP2pIface("p2p0");
+ collector.checkThat("P2P removal success", status, equalTo(true));
+
+ mTestLooper.dispatchAll();
+ verify(p2pDestroyedListener).onDestroyed();
+ verify(nanAvailListener).onAvailableForRequest();
+
+ // Request NAN: expect success now
+ nanIface = validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.STA_CHIP_MODE_ID, // chipModeId
+ IfaceType.NAN, // ifaceTypeToCreate
+ "nan0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ nanDestroyedListener, // destroyedListener
+ nanAvailListener // availableListener
+ );
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener, staAvailListener,
+ nanDestroyedListener, nanAvailListener, p2pDestroyedListener);
+ }
+
+ /**
+ * Validates that when (for some reason) the cache is out-of-sync with the actual chip status
+ * then Wi-Fi is shut-down.
+ */
+ @Test
+ public void testCacheMismatchError() throws Exception {
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener staDestroyedListener = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener staAvailListener = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ HalDeviceManager.InterfaceDestroyedListener nanDestroyedListener = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener nanAvailListener = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ // Request STA
+ IWifiIface staIface = validateInterfaceSequence(chipMock,
+ false, // chipModeValid
+ -1000, // chipModeId (only used if chipModeValid is true)
+ IfaceType.STA, // ifaceTypeToCreate
+ "sta0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ staDestroyedListener, // destroyedListener
+ staAvailListener // availableListener
+ );
+
+ // Request NAN
+ IWifiIface nanIface = validateInterfaceSequence(chipMock,
+ true, // chipModeValid
+ BaselineChip.STA_CHIP_MODE_ID, // chipModeId
+ IfaceType.NAN, // ifaceTypeToCreate
+ "nan0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ nanDestroyedListener, // destroyedListener
+ nanAvailListener // availableListener
+ );
+
+ // fiddle with the "chip" by removing the STA
+ chipMock.interfaceNames.get(IfaceType.STA).remove("sta0");
+
+ // now try to request another NAN
+ nanIface = mDut.createNanIface(nanDestroyedListener, mTestLooper.getLooper());
+ mDut.registerInterfaceAvailableForRequestListener(IfaceType.NAN, nanAvailListener,
+ mTestLooper.getLooper());
+ collector.checkThat("NAN can't be created", nanIface, IsNull.nullValue());
+
+ // verify that Wi-Fi is shut-down: should also get all onDestroyed messages that are
+ // registered (even if they seem out-of-sync to chip)
+ mTestLooper.dispatchAll();
+ verify(mWifiMock, times(2)).stop();
+ verify(mManagerStatusListenerMock, times(2)).onStatusChanged();
+ verify(staDestroyedListener).onDestroyed();
+ verify(nanDestroyedListener).onDestroyed();
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener, staAvailListener,
+ nanDestroyedListener, nanAvailListener);
+ }
+
+ /**
+ * Validates that trying to allocate a STA and then another STA fails. Only one STA at a time
+ * is permitted (by baseline chip).
+ */
+ @Test
+ public void testDuplicateStaRequests() throws Exception {
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceDestroyedListener staDestroyedListener1 = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ HalDeviceManager.InterfaceAvailableForRequestListener staAvailListener1 = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ HalDeviceManager.InterfaceDestroyedListener staDestroyedListener2 = mock(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+
+ // get STA interface
+ IWifiIface staIface1 = validateInterfaceSequence(chipMock,
+ false, // chipModeValid
+ -1000, // chipModeId (only used if chipModeValid is true)
+ IfaceType.STA, // ifaceTypeToCreate
+ "sta0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ staDestroyedListener1, // destroyedListener
+ staAvailListener1 // availableListener
+ );
+ collector.checkThat("STA created", staIface1, IsNull.notNullValue());
+
+ // get STA interface again
+ IWifiIface staIface2 = mDut.createStaIface(staDestroyedListener2, mTestLooper.getLooper());
+ collector.checkThat("STA created", staIface2, IsNull.nullValue());
+
+ verifyNoMoreInteractions(mManagerStatusListenerMock, staDestroyedListener1,
+ staAvailListener1, staDestroyedListener2);
+ }
+
+ /**
+ * Validates that a duplicate registration of the same InterfaceAvailableForRequestListener
+ * listener will result in a single callback.
+ *
+ * Also validates that get an immediate call on registration if available.
+ */
+ @Test
+ public void testDuplicateAvailableRegistrations() throws Exception {
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ HalDeviceManager.InterfaceAvailableForRequestListener staAvailListener = mock(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+
+ // get STA interface
+ IWifiIface staIface = validateInterfaceSequence(chipMock,
+ false, // chipModeValid
+ -1000, // chipModeId (only used if chipModeValid is true)
+ IfaceType.STA, // ifaceTypeToCreate
+ "sta0", // ifaceName
+ BaselineChip.STA_CHIP_MODE_ID, // finalChipMode
+ null, // tearDownList
+ null, // destroyedListener
+ null // availableListener
+ );
+ collector.checkThat("STA created", staIface, IsNull.notNullValue());
+
+ // act: register the same listener twice
+ mDut.registerInterfaceAvailableForRequestListener(IfaceType.STA, staAvailListener,
+ mTestLooper.getLooper());
+ mDut.registerInterfaceAvailableForRequestListener(IfaceType.STA, staAvailListener,
+ mTestLooper.getLooper());
+ mTestLooper.dispatchAll();
+
+ // remove STA interface -> should trigger callbacks
+ mDut.removeIface(staIface);
+ mTestLooper.dispatchAll();
+
+ // verify: only a single trigger
+ verify(staAvailListener).onAvailableForRequest();
+
+ verifyNoMoreInteractions(staAvailListener);
+ }
+
+ /**
+ * Validate that the getSupportedIfaceTypes API works when requesting for all chips.
+ */
+ @Test
+ public void testGetSupportedIfaceTypesAll() throws Exception {
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ // try API
+ Set<Integer> results = mDut.getSupportedIfaceTypes();
+
+ // verify results
+ Set<Integer> correctResults = new HashSet<>();
+ correctResults.add(IfaceType.AP);
+ correctResults.add(IfaceType.STA);
+ correctResults.add(IfaceType.P2P);
+ correctResults.add(IfaceType.NAN);
+
+ assertEquals(correctResults, results);
+ }
+
+ /**
+ * Validate that the getSupportedIfaceTypes API works when requesting for a specific chip.
+ */
+ @Test
+ public void testGetSupportedIfaceTypesOneChip() throws Exception {
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence();
+
+ // try API
+ Set<Integer> results = mDut.getSupportedIfaceTypes(chipMock.chip);
+
+ // verify results
+ Set<Integer> correctResults = new HashSet<>();
+ correctResults.add(IfaceType.AP);
+ correctResults.add(IfaceType.STA);
+ correctResults.add(IfaceType.P2P);
+ correctResults.add(IfaceType.NAN);
+
+ assertEquals(correctResults, results);
+ }
+
+ /**
+ * Validate that when no chip info is found an empty list is returned.
+ */
+ @Test
+ public void testGetSupportedIfaceTypesError() throws Exception {
+ // try API
+ Set<Integer> results = mDut.getSupportedIfaceTypes();
+
+ // verify results
+ assertEquals(0, results.size());
+ }
+
+ /**
+ * Test start HAL can retry upon failure.
+ */
+ @Test
+ public void testStartHalRetryUponNotAvailableFailure() throws Exception {
+ // Override the stubbing for mWifiMock in before().
+ when(mWifiMock.start())
+ .thenReturn(getStatus(WifiStatusCode.ERROR_NOT_AVAILABLE))
+ .thenReturn(mStatusOk);
+
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip,
+ mManagerStatusListenerMock);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence(2, true);
+ }
+
+ /**
+ * Test start HAL fails after multiple retry failures.
+ */
+ @Test
+ public void testStartHalRetryFailUponMultipleNotAvailableFailures() throws Exception {
+ // Override the stubbing for mWifiMock in before().
+ when(mWifiMock.start()).thenReturn(getStatus(WifiStatusCode.ERROR_NOT_AVAILABLE));
+
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence(START_HAL_RETRY_TIMES + 1, false);
+ }
+
+ /**
+ * Test start HAL fails after multiple retry failures.
+ */
+ @Test
+ public void testStartHalRetryFailUponTrueFailure() throws Exception {
+ // Override the stubbing for mWifiMock in before().
+ when(mWifiMock.start()).thenReturn(getStatus(WifiStatusCode.ERROR_UNKNOWN));
+
+ BaselineChip chipMock = new BaselineChip();
+ chipMock.initialize();
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip);
+ executeAndValidateInitializationSequence();
+ executeAndValidateStartupSequence(1, false);
+ }
+
+ /**
+ * Validate that isSupported() returns true when IServiceManager finds the vendor HAL daemon in
+ * the VINTF.
+ */
+ @Test
+ public void testIsSupportedTrue() throws Exception {
+ when(mServiceManagerMock.getTransport(
+ eq(IWifi.kInterfaceName), eq(HalDeviceManager.HAL_INSTANCE_NAME)))
+ .thenReturn(IServiceManager.Transport.HWBINDER);
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock);
+ executeAndValidateInitializationSequence();
+ assertTrue(mDut.isSupported());
+ }
+
+ /**
+ * Validate that isSupported() returns true when IServiceManager finds the vendor HAL daemon in
+ * the VINTF.
+ */
+ @Test
+ public void testIsSupportedFalse() throws Exception {
+ when(mServiceManagerMock.getTransport(
+ eq(IWifi.kInterfaceName), eq(HalDeviceManager.HAL_INSTANCE_NAME)))
+ .thenReturn(IServiceManager.Transport.EMPTY);
+ mInOrder = inOrder(mServiceManagerMock, mWifiMock);
+ executeAndValidateInitializationSequence();
+ assertFalse(mDut.isSupported());
+ }
+
+ // utilities
+ private void dumpDut(String prefix) {
+ StringWriter sw = new StringWriter();
+ mDut.dump(null, new PrintWriter(sw), null);
+ Log.e("HalDeviceManager", prefix + sw.toString());
+ }
+
+ private void executeAndValidateInitializationSequence() throws Exception {
+ // act:
+ mDut.initialize();
+
+ // verify: service manager initialization sequence
+ mInOrder.verify(mServiceManagerMock).linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong());
+ mInOrder.verify(mServiceManagerMock).registerForNotifications(eq(IWifi.kInterfaceName),
+ eq(""), mServiceNotificationCaptor.capture());
+
+ // act: get the service started (which happens even when service was already up)
+ mServiceNotificationCaptor.getValue().onRegistration(IWifi.kInterfaceName, "", true);
+
+ // verify: wifi initialization sequence
+ mInOrder.verify(mWifiMock).linkToDeath(mDeathRecipientCaptor.capture(), anyLong());
+ mInOrder.verify(mWifiMock).registerEventCallback(mWifiEventCallbackCaptor.capture());
+ collector.checkThat("isReady is true", mDut.isReady(), equalTo(true));
+ }
+
+ private void executeAndValidateStartupSequence()throws Exception {
+ executeAndValidateStartupSequence(1, true);
+ }
+
+ private void executeAndValidateStartupSequence(int numAttempts, boolean success)
+ throws Exception {
+ // act: register listener & start Wi-Fi
+ mDut.registerStatusListener(mManagerStatusListenerMock, mTestLooper.getLooper());
+ collector.checkThat(mDut.start(), equalTo(success));
+
+ // verify
+ mInOrder.verify(mWifiMock, times(numAttempts)).start();
+
+ if (success) {
+ // act: trigger onStart callback of IWifiEventCallback
+ mWifiEventCallbackCaptor.getValue().onStart();
+ mTestLooper.dispatchAll();
+
+ // verify: onStart called on registered listener
+ mInOrder.verify(mManagerStatusListenerMock).onStatusChanged();
+ }
+ }
+
+ private IWifiIface validateInterfaceSequence(ChipMockBase chipMock,
+ boolean chipModeValid, int chipModeId,
+ int ifaceTypeToCreate, String ifaceName, int finalChipMode, IWifiIface[] tearDownList,
+ HalDeviceManager.InterfaceDestroyedListener destroyedListener,
+ HalDeviceManager.InterfaceAvailableForRequestListener availableListener,
+ HalDeviceManager.InterfaceDestroyedListener... destroyedInterfacesDestroyedListeners)
+ throws Exception {
+ // configure chip mode response
+ chipMock.chipModeValid = chipModeValid;
+ chipMock.chipModeId = chipModeId;
+
+ IWifiIface iface = null;
+
+ // configure: interface to be created
+ // act: request the interface
+ switch (ifaceTypeToCreate) {
+ case IfaceType.STA:
+ iface = mock(IWifiStaIface.class);
+ doAnswer(new GetNameAnswer(ifaceName)).when(iface).getName(
+ any(IWifiIface.getNameCallback.class));
+ doAnswer(new GetTypeAnswer(IfaceType.STA)).when(iface).getType(
+ any(IWifiIface.getTypeCallback.class));
+ doAnswer(new CreateXxxIfaceAnswer(chipMock, mStatusOk, iface)).when(
+ chipMock.chip).createStaIface(any(IWifiChip.createStaIfaceCallback.class));
+
+ mDut.createStaIface(destroyedListener, mTestLooper.getLooper());
+ break;
+ case IfaceType.AP:
+ iface = mock(IWifiApIface.class);
+ doAnswer(new GetNameAnswer(ifaceName)).when(iface).getName(
+ any(IWifiIface.getNameCallback.class));
+ doAnswer(new GetTypeAnswer(IfaceType.AP)).when(iface).getType(
+ any(IWifiIface.getTypeCallback.class));
+ doAnswer(new CreateXxxIfaceAnswer(chipMock, mStatusOk, iface)).when(
+ chipMock.chip).createApIface(any(IWifiChip.createApIfaceCallback.class));
+
+ mDut.createApIface(destroyedListener, mTestLooper.getLooper());
+ break;
+ case IfaceType.P2P:
+ iface = mock(IWifiP2pIface.class);
+ doAnswer(new GetNameAnswer(ifaceName)).when(iface).getName(
+ any(IWifiIface.getNameCallback.class));
+ doAnswer(new GetTypeAnswer(IfaceType.P2P)).when(iface).getType(
+ any(IWifiIface.getTypeCallback.class));
+ doAnswer(new CreateXxxIfaceAnswer(chipMock, mStatusOk, iface)).when(
+ chipMock.chip).createP2pIface(any(IWifiChip.createP2pIfaceCallback.class));
+
+ mDut.createP2pIface(destroyedListener, mTestLooper.getLooper());
+ break;
+ case IfaceType.NAN:
+ iface = mock(IWifiNanIface.class);
+ doAnswer(new GetNameAnswer(ifaceName)).when(iface).getName(
+ any(IWifiIface.getNameCallback.class));
+ doAnswer(new GetTypeAnswer(IfaceType.NAN)).when(iface).getType(
+ any(IWifiIface.getTypeCallback.class));
+ doAnswer(new CreateXxxIfaceAnswer(chipMock, mStatusOk, iface)).when(
+ chipMock.chip).createNanIface(any(IWifiChip.createNanIfaceCallback.class));
+
+ mDut.createNanIface(destroyedListener, mTestLooper.getLooper());
+ break;
+ }
+ if (availableListener != null) {
+ mDut.registerInterfaceAvailableForRequestListener(ifaceTypeToCreate, availableListener,
+ mTestLooper.getLooper());
+ }
+
+ // validate: optional tear down of interfaces
+ if (tearDownList != null) {
+ for (IWifiIface tearDownIface: tearDownList) {
+ switch (getType(tearDownIface)) {
+ case IfaceType.STA:
+ mInOrder.verify(chipMock.chip).removeStaIface(getName(tearDownIface));
+ break;
+ case IfaceType.AP:
+ mInOrder.verify(chipMock.chip).removeApIface(getName(tearDownIface));
+ break;
+ case IfaceType.P2P:
+ mInOrder.verify(chipMock.chip).removeP2pIface(getName(tearDownIface));
+ break;
+ case IfaceType.NAN:
+ mInOrder.verify(chipMock.chip).removeNanIface(getName(tearDownIface));
+ break;
+ }
+ }
+ }
+
+ // validate: optional switch to the requested mode
+ if (!chipModeValid || chipModeId != finalChipMode) {
+ mInOrder.verify(chipMock.chip).configureChip(finalChipMode);
+ } else {
+ mInOrder.verify(chipMock.chip, times(0)).configureChip(anyInt());
+ }
+
+ // validate: create interface
+ switch (ifaceTypeToCreate) {
+ case IfaceType.STA:
+ mInOrder.verify(chipMock.chip).createStaIface(
+ any(IWifiChip.createStaIfaceCallback.class));
+ break;
+ case IfaceType.AP:
+ mInOrder.verify(chipMock.chip).createApIface(
+ any(IWifiChip.createApIfaceCallback.class));
+ break;
+ case IfaceType.P2P:
+ mInOrder.verify(chipMock.chip).createP2pIface(
+ any(IWifiChip.createP2pIfaceCallback.class));
+ break;
+ case IfaceType.NAN:
+ mInOrder.verify(chipMock.chip).createNanIface(
+ any(IWifiChip.createNanIfaceCallback.class));
+ break;
+ }
+
+ // verify: callbacks on deleted interfaces
+ mTestLooper.dispatchAll();
+ for (int i = 0; i < destroyedInterfacesDestroyedListeners.length; ++i) {
+ verify(destroyedInterfacesDestroyedListeners[i]).onDestroyed();
+ }
+
+ return iface;
+ }
+
+ private int getType(IWifiIface iface) throws Exception {
+ Mutable<Integer> typeResp = new Mutable<>();
+ iface.getType((WifiStatus status, int type) -> {
+ typeResp.value = type;
+ });
+ return typeResp.value;
+ }
+
+ private String getName(IWifiIface iface) throws Exception {
+ Mutable<String> nameResp = new Mutable<>();
+ iface.getName((WifiStatus status, String name) -> {
+ nameResp.value = name;
+ });
+ return nameResp.value;
+ }
+
+ private WifiStatus getStatus(int code) {
+ WifiStatus status = new WifiStatus();
+ status.code = code;
+ return status;
+ }
+
+ private static class Mutable<E> {
+ public E value;
+
+ Mutable() {
+ value = null;
+ }
+
+ Mutable(E value) {
+ this.value = value;
+ }
+ }
+
+ // Answer objects
+ private class GetChipIdsAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private WifiStatus mStatus;
+ private ArrayList<Integer> mChipIds;
+
+ GetChipIdsAnswer(WifiStatus status, ArrayList<Integer> chipIds) {
+ mStatus = status;
+ mChipIds = chipIds;
+ }
+
+ public void answer(IWifi.getChipIdsCallback cb) {
+ cb.onValues(mStatus, mChipIds);
+ }
+ }
+
+ private class GetChipAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private WifiStatus mStatus;
+ private IWifiChip mChip;
+
+ GetChipAnswer(WifiStatus status, IWifiChip chip) {
+ mStatus = status;
+ mChip = chip;
+ }
+
+ public void answer(int chipId, IWifi.getChipCallback cb) {
+ cb.onValues(mStatus, mChip);
+ }
+ }
+
+ private class GetIdAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private ChipMockBase mChipMockBase;
+
+ GetIdAnswer(ChipMockBase chipMockBase) {
+ mChipMockBase = chipMockBase;
+ }
+
+ public void answer(IWifiChip.getIdCallback cb) {
+ cb.onValues(mStatusOk, mChipMockBase.chipId);
+ }
+ }
+
+ private class GetAvailableModesAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private ChipMockBase mChipMockBase;
+
+ GetAvailableModesAnswer(ChipMockBase chipMockBase) {
+ mChipMockBase = chipMockBase;
+ }
+
+ public void answer(IWifiChip.getAvailableModesCallback cb) {
+ cb.onValues(mStatusOk, mChipMockBase.availableModes);
+ }
+ }
+
+ private class GetModeAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private ChipMockBase mChipMockBase;
+
+ GetModeAnswer(ChipMockBase chipMockBase) {
+ mChipMockBase = chipMockBase;
+ }
+
+ public void answer(IWifiChip.getModeCallback cb) {
+ cb.onValues(mChipMockBase.chipModeValid ? mStatusOk
+ : getStatus(WifiStatusCode.ERROR_NOT_AVAILABLE), mChipMockBase.chipModeId);
+ }
+ }
+
+ private class ConfigureChipAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private ChipMockBase mChipMockBase;
+
+ ConfigureChipAnswer(ChipMockBase chipMockBase) {
+ mChipMockBase = chipMockBase;
+ }
+
+ public WifiStatus answer(int chipMode) {
+ mChipMockBase.chipModeId = chipMode;
+ return mStatusOk;
+ }
+ }
+
+ private class GetXxxIfaceNamesAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private ChipMockBase mChipMockBase;
+
+ GetXxxIfaceNamesAnswer(ChipMockBase chipMockBase) {
+ mChipMockBase = chipMockBase;
+ }
+
+ public void answer(IWifiChip.getStaIfaceNamesCallback cb) {
+ cb.onValues(mStatusOk, mChipMockBase.interfaceNames.get(IfaceType.STA));
+ }
+
+ public void answer(IWifiChip.getApIfaceNamesCallback cb) {
+ cb.onValues(mStatusOk, mChipMockBase.interfaceNames.get(IfaceType.AP));
+ }
+
+ public void answer(IWifiChip.getP2pIfaceNamesCallback cb) {
+ cb.onValues(mStatusOk, mChipMockBase.interfaceNames.get(IfaceType.P2P));
+ }
+
+ public void answer(IWifiChip.getNanIfaceNamesCallback cb) {
+ cb.onValues(mStatusOk, mChipMockBase.interfaceNames.get(IfaceType.NAN));
+ }
+ }
+
+ private class GetXxxIfaceAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private ChipMockBase mChipMockBase;
+
+ GetXxxIfaceAnswer(ChipMockBase chipMockBase) {
+ mChipMockBase = chipMockBase;
+ }
+
+ public void answer(String name, IWifiChip.getStaIfaceCallback cb) {
+ IWifiIface iface = mChipMockBase.interfacesByName.get(IfaceType.STA).get(name);
+ cb.onValues(iface != null ? mStatusOk : mStatusFail, (IWifiStaIface) iface);
+ }
+
+ public void answer(String name, IWifiChip.getApIfaceCallback cb) {
+ IWifiIface iface = mChipMockBase.interfacesByName.get(IfaceType.AP).get(name);
+ cb.onValues(iface != null ? mStatusOk : mStatusFail, (IWifiApIface) iface);
+ }
+
+ public void answer(String name, IWifiChip.getP2pIfaceCallback cb) {
+ IWifiIface iface = mChipMockBase.interfacesByName.get(IfaceType.P2P).get(name);
+ cb.onValues(iface != null ? mStatusOk : mStatusFail, (IWifiP2pIface) iface);
+ }
+
+ public void answer(String name, IWifiChip.getNanIfaceCallback cb) {
+ IWifiIface iface = mChipMockBase.interfacesByName.get(IfaceType.NAN).get(name);
+ cb.onValues(iface != null ? mStatusOk : mStatusFail, (IWifiNanIface) iface);
+ }
+ }
+
+ private class CreateXxxIfaceAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private ChipMockBase mChipMockBase;
+ private WifiStatus mStatus;
+ private IWifiIface mWifiIface;
+
+ CreateXxxIfaceAnswer(ChipMockBase chipMockBase, WifiStatus status, IWifiIface wifiIface) {
+ mChipMockBase = chipMockBase;
+ mStatus = status;
+ mWifiIface = wifiIface;
+ }
+
+ private void addInterfaceInfo(int type) {
+ if (mStatus.code == WifiStatusCode.SUCCESS) {
+ try {
+ mChipMockBase.interfaceNames.get(type).add(getName(mWifiIface));
+ mChipMockBase.interfacesByName.get(type).put(getName(mWifiIface), mWifiIface);
+ } catch (Exception e) {
+ // do nothing
+ }
+ }
+ }
+
+ public void answer(IWifiChip.createStaIfaceCallback cb) {
+ cb.onValues(mStatus, (IWifiStaIface) mWifiIface);
+ addInterfaceInfo(IfaceType.STA);
+ }
+
+ public void answer(IWifiChip.createApIfaceCallback cb) {
+ cb.onValues(mStatus, (IWifiApIface) mWifiIface);
+ addInterfaceInfo(IfaceType.AP);
+ }
+
+ public void answer(IWifiChip.createP2pIfaceCallback cb) {
+ cb.onValues(mStatus, (IWifiP2pIface) mWifiIface);
+ addInterfaceInfo(IfaceType.P2P);
+ }
+
+ public void answer(IWifiChip.createNanIfaceCallback cb) {
+ cb.onValues(mStatus, (IWifiNanIface) mWifiIface);
+ addInterfaceInfo(IfaceType.NAN);
+ }
+ }
+
+ private class RemoveXxxIfaceAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private ChipMockBase mChipMockBase;
+ private int mType;
+
+ RemoveXxxIfaceAnswer(ChipMockBase chipMockBase, int type) {
+ mChipMockBase = chipMockBase;
+ mType = type;
+ }
+
+ private WifiStatus removeIface(int type, String ifname) {
+ try {
+ if (!mChipMockBase.interfaceNames.get(type).remove(ifname)) {
+ return mStatusFail;
+ }
+ if (mChipMockBase.interfacesByName.get(type).remove(ifname) == null) {
+ return mStatusFail;
+ }
+ } catch (Exception e) {
+ return mStatusFail;
+ }
+ return mStatusOk;
+ }
+
+ public WifiStatus answer(String ifname) {
+ return removeIface(mType, ifname);
+ }
+ }
+
+ private class GetNameAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private String mName;
+
+ GetNameAnswer(String name) {
+ mName = name;
+ }
+
+ public void answer(IWifiIface.getNameCallback cb) {
+ cb.onValues(mStatusOk, mName);
+ }
+ }
+
+ private class GetTypeAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private int mType;
+
+ GetTypeAnswer(int type) {
+ mType = type;
+ }
+
+ public void answer(IWifiIface.getTypeCallback cb) {
+ cb.onValues(mStatusOk, mType);
+ }
+ }
+
+ // chip configuration
+
+ private class ChipMockBase {
+ public IWifiChip chip;
+ public int chipId;
+ public boolean chipModeValid = false;
+ public int chipModeId = -1000;
+ public Map<Integer, ArrayList<String>> interfaceNames = new HashMap<>();
+ public Map<Integer, Map<String, IWifiIface>> interfacesByName = new HashMap<>();
+
+ public ArrayList<IWifiChip.ChipMode> availableModes;
+
+ void initialize() throws Exception {
+ chip = mock(IWifiChip.class);
+
+ interfaceNames.put(IfaceType.STA, new ArrayList<>());
+ interfaceNames.put(IfaceType.AP, new ArrayList<>());
+ interfaceNames.put(IfaceType.P2P, new ArrayList<>());
+ interfaceNames.put(IfaceType.NAN, new ArrayList<>());
+
+ interfacesByName.put(IfaceType.STA, new HashMap<>());
+ interfacesByName.put(IfaceType.AP, new HashMap<>());
+ interfacesByName.put(IfaceType.P2P, new HashMap<>());
+ interfacesByName.put(IfaceType.NAN, new HashMap<>());
+
+ when(chip.registerEventCallback(any(IWifiChipEventCallback.class))).thenReturn(
+ mStatusOk);
+ when(chip.configureChip(anyInt())).thenAnswer(new ConfigureChipAnswer(this));
+ doAnswer(new GetIdAnswer(this)).when(chip).getId(any(IWifiChip.getIdCallback.class));
+ doAnswer(new GetModeAnswer(this)).when(chip).getMode(
+ any(IWifiChip.getModeCallback.class));
+ GetXxxIfaceNamesAnswer getXxxIfaceNamesAnswer = new GetXxxIfaceNamesAnswer(this);
+ doAnswer(getXxxIfaceNamesAnswer).when(chip).getStaIfaceNames(
+ any(IWifiChip.getStaIfaceNamesCallback.class));
+ doAnswer(getXxxIfaceNamesAnswer).when(chip).getApIfaceNames(
+ any(IWifiChip.getApIfaceNamesCallback.class));
+ doAnswer(getXxxIfaceNamesAnswer).when(chip).getP2pIfaceNames(
+ any(IWifiChip.getP2pIfaceNamesCallback.class));
+ doAnswer(getXxxIfaceNamesAnswer).when(chip).getNanIfaceNames(
+ any(IWifiChip.getNanIfaceNamesCallback.class));
+ GetXxxIfaceAnswer getXxxIfaceAnswer = new GetXxxIfaceAnswer(this);
+ doAnswer(getXxxIfaceAnswer).when(chip).getStaIface(anyString(),
+ any(IWifiChip.getStaIfaceCallback.class));
+ doAnswer(getXxxIfaceAnswer).when(chip).getApIface(anyString(),
+ any(IWifiChip.getApIfaceCallback.class));
+ doAnswer(getXxxIfaceAnswer).when(chip).getP2pIface(anyString(),
+ any(IWifiChip.getP2pIfaceCallback.class));
+ doAnswer(getXxxIfaceAnswer).when(chip).getNanIface(anyString(),
+ any(IWifiChip.getNanIfaceCallback.class));
+ doAnswer(new RemoveXxxIfaceAnswer(this, IfaceType.STA)).when(chip).removeStaIface(
+ anyString());
+ doAnswer(new RemoveXxxIfaceAnswer(this, IfaceType.AP)).when(chip).removeApIface(
+ anyString());
+ doAnswer(new RemoveXxxIfaceAnswer(this, IfaceType.P2P)).when(chip).removeP2pIface(
+ anyString());
+ doAnswer(new RemoveXxxIfaceAnswer(this, IfaceType.NAN)).when(chip).removeNanIface(
+ anyString());
+ }
+ }
+
+ // emulate baseline/legacy config:
+ // mode: STA + NAN || P2P
+ // mode: NAN
+ private class BaselineChip extends ChipMockBase {
+ static final int STA_CHIP_MODE_ID = 0;
+ static final int AP_CHIP_MODE_ID = 1;
+
+ void initialize() throws Exception {
+ super.initialize();
+
+ // chip Id configuration
+ ArrayList<Integer> chipIds;
+ chipId = 10;
+ chipIds = new ArrayList<>();
+ chipIds.add(chipId);
+ doAnswer(new GetChipIdsAnswer(mStatusOk, chipIds)).when(mWifiMock).getChipIds(
+ any(IWifi.getChipIdsCallback.class));
+
+ doAnswer(new GetChipAnswer(mStatusOk, chip)).when(mWifiMock).getChip(eq(10),
+ any(IWifi.getChipCallback.class));
+
+ // initialize dummy chip modes
+ IWifiChip.ChipMode cm;
+ IWifiChip.ChipIfaceCombination cic;
+ IWifiChip.ChipIfaceCombinationLimit cicl;
+
+ // Mode 0: 1xSTA + 1x{P2P,NAN}
+ // Mode 1: 1xAP
+ availableModes = new ArrayList<>();
+ cm = new IWifiChip.ChipMode();
+ cm.id = STA_CHIP_MODE_ID;
+
+ cic = new IWifiChip.ChipIfaceCombination();
+
+ cicl = new IWifiChip.ChipIfaceCombinationLimit();
+ cicl.maxIfaces = 1;
+ cicl.types.add(IfaceType.STA);
+ cic.limits.add(cicl);
+
+ cicl = new IWifiChip.ChipIfaceCombinationLimit();
+ cicl.maxIfaces = 1;
+ cicl.types.add(IfaceType.P2P);
+ cicl.types.add(IfaceType.NAN);
+ cic.limits.add(cicl);
+ cm.availableCombinations.add(cic);
+ availableModes.add(cm);
+
+ cm = new IWifiChip.ChipMode();
+ cm.id = AP_CHIP_MODE_ID;
+ cic = new IWifiChip.ChipIfaceCombination();
+ cicl = new IWifiChip.ChipIfaceCombinationLimit();
+ cicl.maxIfaces = 1;
+ cicl.types.add(IfaceType.AP);
+ cic.limits.add(cicl);
+ cm.availableCombinations.add(cic);
+ availableModes.add(cm);
+
+ doAnswer(new GetAvailableModesAnswer(this)).when(chip)
+ .getAvailableModes(any(IWifiChip.getAvailableModesCallback.class));
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/HalMockUtils.java b/tests/wifitests/src/com/android/server/wifi/HalMockUtils.java
deleted file mode 100644
index 6e485f6..0000000
--- a/tests/wifitests/src/com/android/server/wifi/HalMockUtils.java
+++ /dev/null
@@ -1,143 +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 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.Field;
-import java.util.Iterator;
-
-public class HalMockUtils {
- private static final String TAG = "HalMockUtils";
- private static final boolean DBG = true;
- private static final boolean VDBG = true;
-
- private static native int initHalMock();
-
- public static native void setHalMockObject(Object obj);
-
- static {
- System.loadLibrary("wifi-hal-mock");
- }
-
- 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.
- */
- Field field = WifiNative.class.getDeclaredField("sWifiHalHandle");
- field.setAccessible(true);
- long currentWifiHalHandle = field.getLong(null);
- if (DBG) Log.d(TAG, "currentWifiHalHandle=" + currentWifiHalHandle);
- if (currentWifiHalHandle == 0) {
- field.setLong(null, 5);
-
- field = WifiNative.class.getDeclaredField("sWifiIfaceHandles");
- field.setAccessible(true);
- long[] wifiIfaceHandles = {
- 10 };
- field.set(null, wifiIfaceHandles);
- }
-
- initHalMock();
- }
-
- /*
- * JSON data-model for passing arguments between mock host (java) and mock
- * HAL (C):
- * {
- * "name" : { "type" : "int|byte_array", "value" : 123 | [1, 2, 3, 4] }
- * }
- */
-
- private static final String TYPE_KEY = "type";
- private static final String VALUE_KEY = "value";
-
- private static final String TYPE_INT = "int";
- private static final String TYPE_BYTE_ARRAY = "byte_array";
-
- public static Bundle convertJsonToBundle(String jsonArgs) throws JSONException {
- if (VDBG) Log.v(TAG, "convertJsonToBundle: jsonArgs=" + jsonArgs);
-
- Bundle bundle = new Bundle();
-
- JSONObject jsonObject = new JSONObject(jsonArgs);
- Iterator<String> iter = jsonObject.keys();
- while (iter.hasNext()) {
- String key = iter.next();
- JSONObject field = jsonObject.optJSONObject(key);
-
- String type = field.getString(TYPE_KEY);
-
- if (TYPE_INT.equals(type)) {
- bundle.putInt(key, field.optInt(VALUE_KEY));
- } else if (TYPE_BYTE_ARRAY.equals(type)) {
- JSONArray array = field.optJSONArray(VALUE_KEY);
- byte[] bArray = new byte[array.length()];
- for (int i = 0; i < array.length(); ++i) {
- bArray[i] = (byte) array.getInt(i);
- }
- bundle.putByteArray(key, bArray);
- } else {
- throw new JSONException("Unexpected TYPE read from mock HAL -- '" + type + "'");
- }
- }
-
- if (DBG) Log.d(TAG, "convertJsonToBundle: returning bundle=" + bundle);
- return bundle;
- }
-
- public static JSONObject convertBundleToJson(Bundle bundle) throws JSONException {
- if (VDBG) Log.v(TAG, "convertBundleToJson: bundle=" + bundle.toString());
-
- JSONObject json = new JSONObject();
- for (String key : bundle.keySet()) {
- Object value = bundle.get(key);
- JSONObject child = new JSONObject();
- if (value instanceof Integer) {
- child.put(TYPE_KEY, TYPE_INT);
- child.put(VALUE_KEY, ((Integer) value).intValue());
- } else if (value instanceof byte[]) {
- byte[] array = (byte[]) value;
- JSONArray jsonArray = new JSONArray();
- for (int i = 0; i < array.length; ++i) {
- jsonArray.put(array[i]);
- }
- child.put(TYPE_KEY, TYPE_BYTE_ARRAY);
- child.put(VALUE_KEY, jsonArray);
- } else {
- throw new JSONException("Unexpected type of JSON tree node (not an Integer "
- + "or byte[]): " + value);
- }
- json.put(key, child);
- }
-
- if (DBG) Log.d(TAG, "convertBundleToJson: returning JSONObject=" + json);
- return json;
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java b/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java
new file mode 100644
index 0000000..d99d8a5
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/IMSIParameterTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.IMSIParameter}.
+ */
+@SmallTest
+public class IMSIParameterTest {
+ /**
+ * Data points for testing function {@link IMSIParameter#build}.
+ */
+ private static final Map<String, IMSIParameter> BUILD_PARAM_TEST_MAP = new HashMap<>();
+ static {
+ BUILD_PARAM_TEST_MAP.put(null, null);
+ BUILD_PARAM_TEST_MAP.put("", null);
+ BUILD_PARAM_TEST_MAP.put("1234a123", null); // IMSI contained invalid character.
+ BUILD_PARAM_TEST_MAP.put("1234567890123451", null); // IMSI exceeding max length.
+ BUILD_PARAM_TEST_MAP.put("123456789012345", new IMSIParameter("123456789012345", false));
+ BUILD_PARAM_TEST_MAP.put("1234*", new IMSIParameter("1234", true));
+ }
+
+ /**
+ * Verify the expectations of {@link IMSIParameter#build} function using the predefined
+ * test data {@link #BUILD_PARAM_TEST_MAP}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyBuildIMSIParameter() throws Exception {
+ for (Map.Entry<String, IMSIParameter> entry : BUILD_PARAM_TEST_MAP.entrySet()) {
+ assertEquals(entry.getValue(), IMSIParameter.build(entry.getKey()));
+ }
+ }
+
+ /**
+ * Verify that attempt to match a null IMSI will not cause any crash and should return false.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesNullImsi() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234", false);
+ assertFalse(param.matchesImsi(null));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing a full IMSI will only match against an IMSI of the
+ * same value.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesImsiWithFullImsi() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234", false);
+
+ // Full IMSI requires exact matching.
+ assertFalse(param.matchesImsi("123"));
+ assertFalse(param.matchesImsi("12345"));
+ assertTrue(param.matchesImsi("1234"));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing a prefix IMSI will match against any IMSI that
+ * starts with the same prefix.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesImsiWithPrefixImsi() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234", true);
+
+ // Prefix IMSI will match any IMSI that starts with the same prefix.
+ assertFalse(param.matchesImsi("123"));
+ assertTrue(param.matchesImsi("12345"));
+ assertTrue(param.matchesImsi("1234"));
+ }
+
+ /**
+ * Verify that attempt to match a null MCC-MNC will not cause any crash and should return
+ * false.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesNullMccMnc() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234", false);
+ assertFalse(param.matchesMccMnc(null));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing a full IMSI will only match against a 6 digit
+ * MCC-MNC that is a prefix of the IMSI.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesMccMncFullImsi() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234567890", false);
+
+ assertFalse(param.matchesMccMnc("1234567")); // Invalid length for MCC-MNC
+ assertFalse(param.matchesMccMnc("12345")); // Invalid length for MCC-MNC
+ assertTrue(param.matchesMccMnc("123456"));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing an IMSI prefix that's less than 6 digits
+ * will match against any 6-digit MCC-MNC that starts with the same prefix.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesMccMncWithPrefixImsiLessThanMccMncLength() throws Exception {
+ IMSIParameter param = new IMSIParameter("12345", true);
+
+ assertFalse(param.matchesMccMnc("123448")); // Prefix mismatch
+ assertFalse(param.matchesMccMnc("12345")); // Invalid length for MCC-MNC
+ assertFalse(param.matchesMccMnc("1234567")); // Invalid length for MCC-MNC
+ assertTrue(param.matchesMccMnc("123457"));
+ assertTrue(param.matchesMccMnc("123456"));
+ }
+
+ /**
+ * Verify that an IMSIParameter containing an IMSI prefix that's more than 6 digits
+ * will only match against a 6-digit MCC-MNC that matches the first 6-digit of the prefix.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchesMccMncWithPrefixImsiMoreThanMccMncLength() throws Exception {
+ IMSIParameter param = new IMSIParameter("1234567890", true);
+ assertFalse(param.matchesMccMnc("12345")); // Invalid length for MCC-MNC
+ assertFalse(param.matchesMccMnc("1234567")); // Invalid length for MCC-MNC
+ assertTrue(param.matchesMccMnc("123456"));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/LastMileLoggerTest.java b/tests/wifitests/src/com/android/server/wifi/LastMileLoggerTest.java
new file mode 100644
index 0000000..5c7c5ab
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/LastMileLoggerTest.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.contains;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.os.FileUtils;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import libcore.io.IoUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Unit tests for {@link LastMileLogger}.
+ */
+@SmallTest
+public class LastMileLoggerTest {
+ @Mock WifiInjector mWifiInjector;
+ @Spy FakeWifiLog mLog;
+ private static final long FAKE_CONNECTION_ID = 1;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mWifiInjector.makeLog(anyString())).thenReturn(mLog);
+ mTraceDataFile = File.createTempFile(TRACE_DATA_PREFIX, null);
+ mTraceEnableFile = File.createTempFile(TRACE_ENABLE_PREFIX, null);
+ mTraceReleaseFile = File.createTempFile(TRACE_RELEASE_PREFIX, null);
+ mTraceDataFile.deleteOnExit();
+ mTraceEnableFile.deleteOnExit();
+ mTraceReleaseFile.deleteOnExit();
+ FileUtils.stringToFile(mTraceEnableFile, "0");
+ mLastMileLogger = new LastMileLogger(mWifiInjector, mTraceDataFile.getPath(),
+ mTraceEnableFile.getPath(), mTraceReleaseFile.getPath());
+ }
+
+ @Test
+ public void ctorDoesNotCrash() throws Exception {
+ new LastMileLogger(mWifiInjector, mTraceDataFile.getPath(), mTraceEnableFile.getPath(),
+ mTraceReleaseFile.getPath());
+ verifyZeroInteractions(mLog);
+ }
+
+ @Test
+ public void connectionEventStartedEnablesTracing() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ assertEquals("1", IoUtils.readFileAsString(mTraceEnableFile.getPath()));
+ }
+
+ @Test
+ public void connectionEventStartedDoesNotCrashIfReleaseFileIsMissing() throws Exception {
+ mTraceReleaseFile.delete();
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ verify(mLog).warn(contains("Failed to open free_buffer"));
+ }
+
+ @Test
+ public void connectionEventStartedDoesNotEnableTracingIfReleaseFileIsMissing()
+ throws Exception {
+ mTraceReleaseFile.delete();
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ assertEquals("0", IoUtils.readFileAsString(mTraceEnableFile.getPath()));
+ }
+
+ @Test
+ public void connectionEventStartedDoesNotAttemptToReopenReleaseFile() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+
+ // This is a rather round-about way of verifying that we don't attempt to re-open
+ // the file. Namely: if we delete the |release| file, and CONNECTION_EVENT_STARTED
+ // _did_ re-open the file, then we'd log an error message. Since the test is deleting the
+ // |release| file, the absence of a warning message means that we didn't try to open the
+ // file again.
+ //
+ // A more direct test would require the use of a factory for the creation of the
+ // FileInputStream.
+ mTraceReleaseFile.delete();
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ verifyZeroInteractions(mLog);
+ }
+
+ @Test
+ public void connectionEventStartedDoesNotEnableTracingForInvalidConnectionId()
+ throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ -1, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ assertEquals("0", IoUtils.readFileAsString(mTraceEnableFile.getPath()));
+ }
+
+ @Test
+ public void connectionEventStartedDoesNotCrashIfEnableFileIsMissing() throws Exception {
+ mTraceEnableFile.delete();
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ }
+
+ @Test
+ public void connectionEventStartedDoesNotCrashOnRepeatedCalls() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ }
+
+ @Test
+ public void connectionEventSucceededDisablesTracing() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
+ assertEquals("0", IoUtils.readFileAsString(mTraceEnableFile.getPath()));
+ }
+
+ @Test
+ public void connectionEventSucceededDoesNotCrashIfEnableFileIsMissing() throws Exception {
+ mTraceEnableFile.delete();
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
+ }
+
+ @Test
+ public void connectionEventSucceededDoesNotCrashOnRepeatedCalls() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
+ }
+
+ @Test
+ public void connectionEventFailedDisablesTracingWhenPendingFails() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ assertEquals("0", IoUtils.readFileAsString(mTraceEnableFile.getPath()));
+ }
+
+ @Test
+ public void connectionEventFailedDoesNotDisableTracingOnFailureOfStaleConnection()
+ throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID + 1, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ assertEquals("1", IoUtils.readFileAsString(mTraceEnableFile.getPath()));
+ }
+
+ @Test
+ public void connectionEventFailedDisablesTracingOnFailureOfFutureConnection()
+ throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID + 1, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ assertEquals("0", IoUtils.readFileAsString(mTraceEnableFile.getPath()));
+ }
+
+ @Test
+ public void connectionEventFailedDoesNotCrashIfEnableFileIsMissing() throws Exception {
+ mTraceEnableFile.delete();
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ }
+
+ @Test
+ public void connectionEventFailedDoesNotCrashIfDataFileIsMissing() throws Exception {
+ mTraceDataFile.delete();
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ }
+
+ @Test
+ public void connectionEventFailedDoesNotCrashOnRepeatedCalls() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ }
+
+ @Test
+ public void dumpShowsFailureTrace() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ FileUtils.stringToFile(mTraceDataFile.getPath(), "rdev_connect");
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ assertTrue(getDumpString().contains("--- Last failed"));
+ assertTrue(getDumpString().contains("rdev_connect"));
+ }
+
+ @Test
+ public void dumpShowsFailureTraceEvenIfConnectionIdIncreases() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ FileUtils.stringToFile(mTraceDataFile.getPath(), "rdev_connect");
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID + 1, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ assertTrue(getDumpString().contains("--- Last failed"));
+ assertTrue(getDumpString().contains("rdev_connect"));
+ }
+
+ @Test
+ public void dumpShowsPendingConnectionTrace() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ FileUtils.stringToFile(mTraceDataFile.getPath(), "rdev_connect");
+ assertTrue(getDumpString().contains("No last mile log for \"Last failed"));
+ assertTrue(getDumpString().contains("--- Latest"));
+ assertTrue(getDumpString().contains("rdev_connect"));
+ }
+
+ @Test
+ public void dumpShowsLastFailureTraceAndPendingConnectionTrace() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ FileUtils.stringToFile(mTraceDataFile.getPath(), "rdev_connect try #1");
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ FileUtils.stringToFile(mTraceDataFile.getPath(), "rdev_connect try #2");
+
+ String dumpString = getDumpString();
+ assertTrue(dumpString.contains("rdev_connect try #1"));
+ assertTrue(dumpString.contains("rdev_connect try #2"));
+ }
+
+ @Test
+ public void dumpShowsLastFailureTraceAndCurrentConnectionTrace() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ FileUtils.stringToFile(mTraceDataFile.getPath(), "rdev_connect try #1");
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ FileUtils.stringToFile(mTraceDataFile.getPath(), "rdev_connect try #2");
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
+
+ String dumpString = getDumpString();
+ assertTrue(dumpString.contains("rdev_connect try #1"));
+ assertTrue(dumpString.contains("rdev_connect try #2"));
+ }
+
+ @Test
+ public void dumpDoesNotClearLastFailureData() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ FileUtils.stringToFile(mTraceDataFile.getPath(), "rdev_connect");
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_FAILED);
+
+ getDumpString();
+ String dumpString = getDumpString();
+ assertTrue(dumpString.contains("rdev_connect"));
+ }
+
+ @Test
+ public void dumpDoesNotClearPendingConnectionTrace() throws Exception {
+ mLastMileLogger.reportConnectionEvent(
+ FAKE_CONNECTION_ID, BaseWifiDiagnostics.CONNECTION_EVENT_STARTED);
+ FileUtils.stringToFile(mTraceDataFile.getPath(), "rdev_connect");
+
+ getDumpString();
+ String dumpString = getDumpString();
+ assertTrue(dumpString.contains("rdev_connect"));
+ }
+
+ @Test
+ public void dumpDoesNotCrashIfDataFileIsEmpty() throws Exception {
+ getDumpString();
+ }
+
+ @Test
+ public void dumpDoesNotCrashIfDataFileIsMissing() throws Exception {
+ mTraceDataFile.delete();
+ getDumpString();
+ }
+
+ private static final String TRACE_DATA_PREFIX = "last-mile-logger-trace-data";
+ private static final String TRACE_ENABLE_PREFIX = "last-mile-logger-trace-enable";
+ private static final String TRACE_RELEASE_PREFIX = "last-mile-logger-trace-release";
+ private LastMileLogger mLastMileLogger;
+ private File mTraceDataFile;
+ private File mTraceEnableFile;
+ private File mTraceReleaseFile;
+
+ private String getDumpString() {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mLastMileLogger.dump(pw);
+ return sw.toString();
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java b/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java
new file mode 100644
index 0000000..5f170b8
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/LocalOnlyHotspotRequestInfoTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.*;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+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 com.android.server.wifi.LocalOnlyHotspotRequestInfo}.
+ */
+@SmallTest
+public class LocalOnlyHotspotRequestInfoTest {
+
+ private static final String TAG = "LocalOnlyHotspotRequestInfoTest";
+ @Mock IBinder mAppBinder;
+ @Mock LocalOnlyHotspotRequestInfo.RequestingApplicationDeathCallback mCallback;
+ private Handler mHandler;
+ private Messenger mMessenger;
+ private TestLooper mTestLooper;
+ RemoteException mRemoteException;
+ private LocalOnlyHotspotRequestInfo mLOHSRequestInfo;
+
+ /**
+ * Setup test.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTestLooper = new TestLooper();
+ mHandler = new Handler(mTestLooper.getLooper());
+ mMessenger = new Messenger(mHandler);
+ mRemoteException = new RemoteException("Test Remote Exception");
+ }
+
+ /**
+ * Make sure we link the call to request LocalOnlyHotspot by an app is linked to their Binder
+ * call. This allows us to clean up if the app dies.
+ */
+ @Test
+ public void verifyBinderLinkToDeathIsCalled() throws Exception {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback);
+ verify(mAppBinder).linkToDeath(eq(mLOHSRequestInfo), eq(0));
+ }
+
+ /**
+ * Calls to create the requestor to binder death should not pass null callback.
+ */
+ @Test(expected = NullPointerException.class)
+ public void verifyNullCallbackChecked() throws Exception {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, null);
+ }
+
+ /**
+ * Calls to create the request info object should not pass a null messenger.
+ */
+ @Test(expected = NullPointerException.class)
+ public void verifyNullMessengerChecked() throws Exception {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, null, mCallback);
+ }
+
+ /**
+ * Calls to link the requestor to binder death should not pass null binder
+ */
+ @Test(expected = NullPointerException.class)
+ public void verifyNullBinderChecked() throws Exception {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(null, mMessenger, mCallback);
+ }
+
+ /**
+ * Calls to unlink the DeathRecipient should call to unlink from Binder.
+ */
+ @Test
+ public void verifyUnlinkDeathRecipientUnlinksFromBinder() throws Exception {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback);
+ mLOHSRequestInfo.unlinkDeathRecipient();
+ verify(mAppBinder).unlinkToDeath(eq(mLOHSRequestInfo), eq(0));
+ }
+
+ /**
+ * Binder death notification should trigger a callback on the requestor.
+ */
+ @Test
+ public void verifyBinderDeathTriggersCallback() {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback);
+ mLOHSRequestInfo.binderDied();
+ verify(mCallback).onLocalOnlyHotspotRequestorDeath(eq(mLOHSRequestInfo));
+ }
+
+ /**
+ * Verify a RemoteException when calling linkToDeath triggers the callback.
+ */
+ @Test
+ public void verifyRemoteExceptionTriggersCallback() throws Exception {
+ doThrow(mRemoteException).when(mAppBinder)
+ .linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback);
+ verify(mCallback).onLocalOnlyHotspotRequestorDeath(eq(mLOHSRequestInfo));
+ }
+
+ /**
+ * Verify the pid is properly set.
+ */
+ @Test
+ public void verifyPid() {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback);
+ assertEquals(Process.myPid(), mLOHSRequestInfo.getPid());
+ }
+
+ /**
+ * Verify that sendHotspotFailedMessage does send a Message properly
+ */
+ @Test
+ public void verifySendFailedMessage() throws Exception {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback);
+ mLOHSRequestInfo.sendHotspotFailedMessage(
+ WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC);
+ Message message = mTestLooper.nextMessage();
+ assertEquals(WifiManager.HOTSPOT_FAILED, message.what);
+ assertEquals(WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC, message.arg1);
+ }
+
+ /**
+ * Verify that sendHotspotStartedMessage does send a Message properly
+ */
+ @Test
+ public void verifySendStartedMessage() throws Exception {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback);
+ WifiConfiguration config = mock(WifiConfiguration.class);
+ mLOHSRequestInfo.sendHotspotStartedMessage(config);
+ Message message = mTestLooper.nextMessage();
+ assertEquals(WifiManager.HOTSPOT_STARTED, message.what);
+ assertEquals(config, (WifiConfiguration) message.obj);
+ }
+
+ /**
+ * Verify that sendHotspotStoppedMessage does send a Message properly
+ */
+ @Test
+ public void verifySendStoppedMessage() throws Exception {
+ mLOHSRequestInfo = new LocalOnlyHotspotRequestInfo(mAppBinder, mMessenger, mCallback);
+ mLOHSRequestInfo.sendHotspotStoppedMessage();
+ Message message = mTestLooper.nextMessage();
+ assertEquals(WifiManager.HOTSPOT_STOPPED, message.what);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/LogcatLogTest.java b/tests/wifitests/src/com/android/server/wifi/LogcatLogTest.java
new file mode 100644
index 0000000..ac5afe7
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/LogcatLogTest.java
@@ -0,0 +1,256 @@
+/*
+ * 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.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link LogcatLog}.
+ */
+@SmallTest
+public class LogcatLogTest {
+ private static final String TAG = "LogcatLogTest";
+ private LogcatLog mLogger;
+
+ /** Initializes test fixture. */
+ @Before
+ public void setUp() {
+ mLogger = new LogcatLog(TAG);
+ }
+
+ /**
+ * Verifies that LogcatLog's LogMessage implementation correctly
+ * handles a format with no parameters.
+ *
+ * Note: In practice, we expect clients to use eC() and friends
+ * when the message is a literal. But we still want to make sure
+ * this functionality works.
+ */
+ @Test
+ public void logMessageWorksWithParameterlessFormat() {
+ WifiLog.LogMessage logMessage = mLogger.err("hello world");
+ logMessage.flush();
+ assertEquals("hello world", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage works with an empty format. */
+ @Test
+ public void logMessageWorksWithEmptyFormat() {
+ WifiLog.LogMessage logMessage = mLogger.err("");
+ logMessage.flush();
+ assertEquals("", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage works with a value-only format. */
+ @Test
+ public void logMessageWorksWithValueOnly() {
+ WifiLog.LogMessage logMessage = mLogger.err("%");
+ logMessage.c(1).flush();
+ assertEquals("1", logMessage.toString());
+ }
+
+ /**
+ * Verifies that LogMessage works when the placeholder is replaced
+ * by the placeholder character.
+ */
+ @Test
+ public void logMessageIsNotConfusedByPlaceholderInValue() {
+ WifiLog.LogMessage logMessage = mLogger.err("%");
+ logMessage.c('%').flush();
+ assertEquals("%", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage works when a value is at the start of the format. */
+ @Test
+ public void logMessageWorksWithValueAtBegin() {
+ WifiLog.LogMessage logMessage = mLogger.err("%stuff");
+ logMessage.c(1).flush();
+ assertEquals("1stuff", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage works when a value is in the middle of the format. */
+ @Test
+ public void logMessageWorksWithValueInMiddle() {
+ WifiLog.LogMessage logMessage = mLogger.err("s%uff");
+ logMessage.c(1).flush();
+ assertEquals("s1uff", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage works when a value is at the end of the format. */
+ @Test
+ public void logMessageWorksWithValueAtEnd() {
+ WifiLog.LogMessage logMessage = mLogger.err("stuff%");
+ logMessage.c(1).flush();
+ assertEquals("stuff1", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage works when a format has multiple values. */
+ @Test
+ public void logMessageWorksWithMultipleValues() {
+ WifiLog.LogMessage logMessage = mLogger.err("% %");
+ logMessage.c("hello").c("world").flush();
+ assertEquals("hello world", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage works when a format has multiple values and literals. */
+ @Test
+ public void logMessageWorksWithMultipleValuesAndLiterals() {
+ WifiLog.LogMessage logMessage = mLogger.err("first:% second:%");
+ logMessage.c("hello").c("world").flush();
+ assertEquals("first:hello second:world", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage works when a format has multiple adjacent values. */
+ @Test
+ public void logMessageWorksWithAdjacentValues() {
+ WifiLog.LogMessage logMessage = mLogger.err("%%");
+ logMessage.c("hello").c("world").flush();
+ assertEquals("helloworld", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage silently ignores extraneous values. */
+ @Test
+ public void logMessageSilentlyIgnoresExtraneousValues() {
+ WifiLog.LogMessage logMessage = mLogger.err("%");
+ logMessage.c("hello world");
+ logMessage.c("more stuff");
+ logMessage.flush();
+ assertEquals("hello world", logMessage.toString());
+ }
+
+ /**
+ * Verifies that LogMessage silently ignores extraneous values,
+ * even with an empty format string.
+ */
+ @Test
+ public void logMessageSilentlyIgnoresExtraneousValuesEvenForEmptyFormat() {
+ WifiLog.LogMessage logMessage = mLogger.err("");
+ logMessage.c("hello world");
+ logMessage.c("more stuff");
+ logMessage.flush();
+ assertEquals("", logMessage.toString());
+ }
+
+ /**
+ * Verifies that LogMessage silently ignores extraneous values,
+ * even if the format string is all literals.
+ */
+ @Test
+ public void logMessageSilentlyIgnoresExtraneousValuesEvenForFormatWithoutPlaceholders() {
+ WifiLog.LogMessage logMessage = mLogger.err("literal format");
+ logMessage.c("hello world");
+ logMessage.c("more stuff");
+ logMessage.flush();
+ assertEquals("literal format", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage copies an unused placeholder to output. */
+ @Test
+ public void logMessageCopiesUnusedPlaceholderToOutput() {
+ WifiLog.LogMessage logMessage = mLogger.err("%");
+ logMessage.flush();
+ assertEquals("%", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage copies multiple unused placeholders to output. */
+ @Test
+ public void logMessageCopiesMultipleUnusedPlaceholdersToOutput() {
+ WifiLog.LogMessage logMessage = mLogger.err("%%%%%");
+ logMessage.flush();
+ assertEquals("%%%%%", logMessage.toString());
+ }
+
+ /**
+ * Verifies that LogMessage copies an unused placeholder to output,
+ * even if preceded by non-placeholders.
+ */
+ @Test
+ public void logMessageCopiesUnusedPlaceholderAtEndToOutput() {
+ WifiLog.LogMessage logMessage = mLogger.err("foo%");
+ logMessage.flush();
+ assertEquals("foo%", logMessage.toString());
+ }
+
+ /**
+ * Verifies that LogMessage copies an unused placeholder to output,
+ * even if followed by non-placeholders.
+ */
+ @Test
+ public void logMessageCopiesUnusedPlaceholderAtBeginToOutput() {
+ WifiLog.LogMessage logMessage = mLogger.err("%foo");
+ logMessage.flush();
+ assertEquals("%foo", logMessage.toString());
+ }
+
+ /**
+ * Verifies that LogMessage copies an unused placeholder to output,
+ * even if it is in the middle of non-placeholders.
+ */
+ @Test
+ public void logMessageCopiesUnusedPlaceholderInMiddleToOutput() {
+ WifiLog.LogMessage logMessage = mLogger.err("f%o");
+ logMessage.flush();
+ assertEquals("f%o", logMessage.toString());
+ }
+
+ /**
+ * Verifies that LogMessage copies multiple unused placeholders to output,
+ * even if they are embedded amongst non-placeholders.
+ */
+ @Test
+ public void logMessageCopiesUnusedPlaceholdersInMiddleToOutput() {
+ WifiLog.LogMessage logMessage = mLogger.err("f%o%o%d");
+ logMessage.flush();
+ assertEquals("f%o%o%d", logMessage.toString());
+ }
+
+ /**
+ * Verifies that LogMessage preserves meta-characters in format string.
+ *
+ * Note that we deliberately test only the meta-characters that we
+ * expect to find in log messages. (Newline might also be
+ * preserved, but clients shouldn't depend on that, as messages
+ * that have newlines make logs hard to read.)
+ */
+ @Test
+ public void logMessagePreservesMetaCharactersInFormat() {
+ WifiLog.LogMessage logMessage = mLogger.err("\\hello\tworld\\");
+ logMessage.flush();
+ assertEquals("\\hello\tworld\\", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage propagates meta-characters in char values. */
+ @Test
+ public void logMessagePropagatesMetaCharactersInCharValues() {
+ WifiLog.LogMessage logMessage = mLogger.err("hello%big%world");
+ logMessage.c('\t').c('\\').flush();
+ assertEquals("hello\tbig\\world", logMessage.toString());
+ }
+
+ /** Verifies that LogMessage propagates meta-characters in String values. */
+ @Test
+ public void logMessagePropagatesMetaCharactersInStringValues() {
+ WifiLog.LogMessage logMessage = mLogger.err("%%world");
+ logMessage.c("hello\t").c("big\\").flush();
+ assertEquals("hello\tbig\\world", logMessage.toString());
+ }
+}
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..fc3418c 100644
--- a/tests/wifitests/src/com/android/server/wifi/MockWifiMonitor.java
+++ b/tests/wifitests/src/com/android/server/wifi/MockWifiMonitor.java
@@ -17,52 +17,40 @@
package com.android.server.wifi;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
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;
/**
- * Creates a WifiMonitor and installs it as the current WifiMonitor instance
+ * Creates a mock WifiMonitor.
* WARNING: This does not perfectly mock the behavior of WifiMonitor at the moment
- * ex. startMoniroting does nothing and will not send a connection/disconnection event
+ * ex. startMonitoring does nothing and will not send a connection/disconnection event
*/
-public class MockWifiMonitor {
- private final WifiMonitor mWifiMonitor;
+public class MockWifiMonitor extends WifiMonitor {
+ private final Map<String, SparseArray<Handler>> mHandlerMap = new HashMap<>();
- public MockWifiMonitor() throws Exception {
- mWifiMonitor = mock(WifiMonitor.class);
-
- Field field = WifiMonitor.class.getDeclaredField("sWifiMonitor");
- field.setAccessible(true);
- field.set(null, mWifiMonitor);
-
- doAnswer(new RegisterHandlerAnswer())
- .when(mWifiMonitor).registerHandler(anyString(), anyInt(), any(Handler.class));
-
+ public MockWifiMonitor() {
+ super(mock(WifiInjector.class));
}
- private final Map<String, SparseArray<Handler>> mHandlerMap = new HashMap<>();
- private class RegisterHandlerAnswer extends AnswerWithArguments {
- public void answer(String iface, int what, Handler handler) {
- SparseArray<Handler> ifaceHandlers = mHandlerMap.get(iface);
- if (ifaceHandlers == null) {
- ifaceHandlers = new SparseArray<>();
- mHandlerMap.put(iface, ifaceHandlers);
- }
- ifaceHandlers.put(what, handler);
+ @Override
+ public void registerHandler(String iface, int what, Handler handler) {
+ SparseArray<Handler> ifaceHandlers = mHandlerMap.get(iface);
+ if (ifaceHandlers == null) {
+ ifaceHandlers = new SparseArray<>();
+ mHandlerMap.put(iface, ifaceHandlers);
}
+ ifaceHandlers.put(what, handler);
+ }
+
+ @Override
+ public synchronized void startMonitoring(String iface, boolean isStaIface) {
+ return;
}
/**
@@ -71,6 +59,7 @@
public void sendMessage(String iface, int what) {
sendMessage(iface, Message.obtain(null, what));
}
+
public void sendMessage(String iface, Message message) {
SparseArray<Handler> ifaceHandlers = mHandlerMap.get(iface);
if (ifaceHandlers != null) {
@@ -87,6 +76,7 @@
+ ",what=" + message.what, sent);
}
}
+
private boolean sendMessage(SparseArray<Handler> ifaceHandlers, Message message) {
Handler handler = ifaceHandlers.get(message.what);
if (handler != null) {
diff --git a/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
new file mode 100644
index 0000000..01257c1
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/NetworkListStoreDataTest.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.util.XmlUtilTest;
+
+import org.junit.Before;
+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.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.NetworksListStoreData}.
+ */
+@SmallTest
+public class NetworkListStoreDataTest {
+
+ 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 String SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT =
+ "<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"
+ + "<boolean name=\"RequirePMF\" 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"
+ + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n"
+ + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n"
+ + "<boolean name=\"Shared\" value=\"%s\" />\n"
+ + "<int name=\"Status\" value=\"2\" />\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"
+ + "<boolean name=\"IsLegacyPasspointConfig\" value=\"false\" />\n"
+ + "<long-array name=\"RoamingConsortiumOIs\" num=\"0\" />\n"
+ + "</WifiConfiguration>\n"
+ + "<NetworkStatus>\n"
+ + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n"
+ + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\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";
+
+ private static final String SINGLE_EAP_NETWORK_DATA_XML_STRING_FORMAT =
+ "<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"
+ + "<boolean name=\"RequirePMF\" value=\"false\" />\n"
+ + "<byte-array name=\"AllowedKeyMgmt\" num=\"1\">0c</byte-array>\n"
+ + "<byte-array name=\"AllowedProtocols\" num=\"0\"></byte-array>\n"
+ + "<byte-array name=\"AllowedAuthAlgos\" num=\"0\"></byte-array>\n"
+ + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n"
+ + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n"
+ + "<boolean name=\"Shared\" value=\"%s\" />\n"
+ + "<int name=\"Status\" value=\"2\" />\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"
+ + "<boolean name=\"IsLegacyPasspointConfig\" value=\"false\" />\n"
+ + "<long-array name=\"RoamingConsortiumOIs\" num=\"0\" />\n"
+ + "</WifiConfiguration>\n"
+ + "<NetworkStatus>\n"
+ + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n"
+ + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\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"
+ + "<WifiEnterpriseConfiguration>\n"
+ + "<string name=\"Identity\"></string>\n"
+ + "<string name=\"AnonIdentity\"></string>\n"
+ + "<string name=\"Password\"></string>\n"
+ + "<string name=\"ClientCert\"></string>\n"
+ + "<string name=\"CaCert\"></string>\n"
+ + "<string name=\"SubjectMatch\"></string>\n"
+ + "<string name=\"Engine\"></string>\n"
+ + "<string name=\"EngineId\"></string>\n"
+ + "<string name=\"PrivateKeyId\"></string>\n"
+ + "<string name=\"AltSubjectMatch\"></string>\n"
+ + "<string name=\"DomSuffixMatch\"></string>\n"
+ + "<string name=\"CaPath\"></string>\n"
+ + "<int name=\"EapMethod\" value=\"2\" />\n"
+ + "<int name=\"Phase2Method\" value=\"0\" />\n"
+ + "<string name=\"PLMN\"></string>\n"
+ + "<string name=\"Realm\"></string>\n"
+ + "</WifiEnterpriseConfiguration>\n"
+ + "</Network>\n";
+
+ private NetworkListStoreData mNetworkListStoreData;
+
+ @Before
+ public void setUp() throws Exception {
+ mNetworkListStoreData = new NetworkListStoreData();
+ }
+
+ /**
+ * Helper function for serializing configuration data to a XML block.
+ *
+ * @param shared Flag indicating serializing shared or user configurations
+ * @return byte[] of the XML data
+ * @throws Exception
+ */
+ private byte[] serializeData(boolean shared) throws Exception {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ mNetworkListStoreData.serializeData(out, shared);
+ out.flush();
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * Helper function for parsing configuration data from a XML block.
+ *
+ * @param data XML data to parse from
+ * @param shared Flag indicating parsing of shared or user configurations
+ * @return List of WifiConfiguration parsed
+ * @throws Exception
+ */
+ private List<WifiConfiguration> deserializeData(byte[] data, boolean shared) throws Exception {
+ final XmlPullParser in = Xml.newPullParser();
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ mNetworkListStoreData.deserializeData(in, in.getDepth(), shared);
+ if (shared) {
+ return mNetworkListStoreData.getSharedConfigurations();
+ } else {
+ return mNetworkListStoreData.getUserConfigurations();
+ }
+ }
+
+ /**
+ * Helper function for generating a network list for testing purpose. The network list
+ * will contained an open and an EAP network.
+ *
+ * @param shared Flag indicating shared network
+ * @return List of WifiConfiguration
+ */
+ private List<WifiConfiguration> getTestNetworksConfig(boolean shared) {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ openNetwork.shared = shared;
+ openNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
+ WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork();
+ eapNetwork.shared = shared;
+ eapNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
+ List<WifiConfiguration> networkList = new ArrayList<>();
+ networkList.add(openNetwork);
+ networkList.add(eapNetwork);
+ return networkList;
+ }
+
+ /**
+ * Helper function for generating XML block containing two networks, an open and an EAP
+ * network.
+ *
+ * @param openNetwork The WifiConfiguration for an open network
+ * @param eapNetwork The WifiConfiguration for an EAP network
+ * @return byte[] of the XML data
+ */
+ private byte[] getTestNetworksXmlBytes(WifiConfiguration openNetwork,
+ WifiConfiguration eapNetwork) {
+ String openNetworkXml = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT,
+ openNetwork.configKey().replaceAll("\"", """),
+ openNetwork.SSID.replaceAll("\"", """),
+ openNetwork.shared, openNetwork.creatorUid);
+ String eapNetworkXml = String.format(SINGLE_EAP_NETWORK_DATA_XML_STRING_FORMAT,
+ eapNetwork.configKey().replaceAll("\"", """),
+ eapNetwork.SSID.replaceAll("\"", """),
+ eapNetwork.shared, eapNetwork.creatorUid);
+ return (openNetworkXml + eapNetworkXml).getBytes(StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Verify that serializing the store data without any configuration doesn't cause any crash
+ * and no data should be serialized.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void serializeEmptyConfigs() throws Exception {
+ assertEquals(0, serializeData(true /* shared */).length);
+ assertEquals(0, serializeData(false /* shared */).length);
+ }
+
+ /**
+ * Verify that parsing an empty data doesn't cause any crash and no configuration should
+ * be parsed.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void deserializeEmptyData() throws Exception {
+ assertTrue(deserializeData(new byte[0], true /* shared */).isEmpty());
+ assertTrue(deserializeData(new byte[0], false /* shared */).isEmpty());
+ }
+
+ /**
+ * Verify that NetworkListStoreData does support share data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void supportShareData() throws Exception {
+ assertTrue(mNetworkListStoreData.supportShareData());
+ }
+
+ /**
+ * Verify that the shared configurations (containing an open and an EAP network) are serialized
+ * correctly, matching the expected XML string.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void serializeSharedConfigurations() throws Exception {
+ List<WifiConfiguration> networkList = getTestNetworksConfig(true /* shared */);
+ mNetworkListStoreData.setSharedConfigurations(networkList);
+ byte[] expectedData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1));
+ assertTrue(Arrays.equals(expectedData, serializeData(true /* shared */)));
+ }
+
+ /**
+ * Verify that the shared configurations are parsed correctly from a XML string containing
+ * test networks (an open and an EAP network).
+ * @throws Exception
+ */
+ @Test
+ public void deserializeSharedConfigurations() throws Exception {
+ List<WifiConfiguration> networkList = getTestNetworksConfig(true /* shared */);
+ byte[] xmlData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1));
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore(
+ networkList, deserializeData(xmlData, true /* shared */));
+ }
+
+ /**
+ * Verify that the user configurations (containing an open and an EAP network) are serialized
+ * correctly, matching the expected XML string.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void serializeUserConfigurations() throws Exception {
+ List<WifiConfiguration> networkList = getTestNetworksConfig(false /* shared */);
+ mNetworkListStoreData.setUserConfigurations(networkList);
+ byte[] expectedData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1));
+ assertTrue(Arrays.equals(expectedData, serializeData(false /* shared */)));
+ }
+
+ /**
+ * Verify that the user configurations are parsed correctly from a XML string containing
+ * test networks (an open and an EAP network).
+ * @throws Exception
+ */
+ @Test
+ public void deserializeUserConfigurations() throws Exception {
+ List<WifiConfiguration> networkList = getTestNetworksConfig(false /* shared */);
+ byte[] xmlData = getTestNetworksXmlBytes(networkList.get(0), networkList.get(1));
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore(
+ networkList, deserializeData(xmlData, false /* shared */));
+ }
+
+ /**
+ * Verify that a XmlPullParserException will be thrown when parsing a <Network> block
+ * containing an unknown tag.
+ *
+ * @throws Exception
+ */
+ @Test(expected = XmlPullParserException.class)
+ public void parseNetworkWithUnknownTag() throws Exception {
+ String configFormat =
+ "<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"
+ + "<boolean name=\"RequirePMF\" 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"
+ + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n"
+ + "<byte-array name=\"AllowedPairwiseCiphers\" 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"
+ + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n"
+ + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\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"
+ + "<Unknown>" // Unknown tag.
+ + "<int name=\"test\" value=\"0\" />\n"
+ + "</Unknown>"
+ + "</Network>\n";
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ byte[] xmlData = String.format(configFormat,
+ openNetwork.configKey().replaceAll("\"", """),
+ openNetwork.SSID.replaceAll("\"", """),
+ openNetwork.shared, openNetwork.creatorUid).getBytes(StandardCharsets.UTF_8);
+ deserializeData(xmlData, true);
+ }
+
+ /**
+ * Verify that a XmlPullParseException will be thrown when parsing a network configuration
+ * containing a mismatched config key.
+ *
+ * @throws Exception
+ */
+ @Test(expected = XmlPullParserException.class)
+ public void parseNetworkWithMismatchConfigKey() throws Exception {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ byte[] xmlData = String.format(SINGLE_OPEN_NETWORK_DATA_XML_STRING_FORMAT,
+ "InvalidConfigKey",
+ openNetwork.SSID.replaceAll("\"", """),
+ openNetwork.shared, openNetwork.creatorUid).getBytes(StandardCharsets.UTF_8);
+ deserializeData(xmlData, true);
+ }
+
+ /**
+ * Tests that an invalid data in one of the WifiConfiguration object parsing would be skipped
+ * gracefully. The other networks in the XML should still be parsed out correctly.
+ */
+ @Test
+ public void parseNetworkListWithOneNetworkIllegalArgException() throws Exception {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork();
+ String xmlString = new String(getTestNetworksXmlBytes(openNetwork, eapNetwork));
+ // Manipulate the XML data to set the EAP method to None, this should raise an Illegal
+ // argument exception in WifiEnterpriseConfig.setEapMethod().
+ xmlString = xmlString.replaceAll(
+ String.format(XmlUtilTest.XML_STRING_EAP_METHOD_REPLACE_FORMAT,
+ eapNetwork.enterpriseConfig.getEapMethod()),
+ String.format(XmlUtilTest.XML_STRING_EAP_METHOD_REPLACE_FORMAT,
+ WifiEnterpriseConfig.Eap.NONE));
+ List<WifiConfiguration> retrievedNetworkList =
+ deserializeData(xmlString.getBytes(StandardCharsets.UTF_8), true /* shared */);
+ // Retrieved network should not contain the eap network.
+ assertEquals(1, retrievedNetworkList.size());
+ for (WifiConfiguration network : retrievedNetworkList) {
+ assertNotEquals(eapNetwork.SSID, network.SSID);
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/PasspointManagementObjectManagerTest.java b/tests/wifitests/src/com/android/server/wifi/PasspointManagementObjectManagerTest.java
deleted file mode 100644
index d3022b9..0000000
--- a/tests/wifitests/src/com/android/server/wifi/PasspointManagementObjectManagerTest.java
+++ /dev/null
@@ -1,329 +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 static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-
-import android.net.wifi.PasspointManagementObjectDefinition;
-import android.net.wifi.WifiEnterpriseConfig;
-import android.security.KeyStore;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.server.wifi.hotspot2.omadm.MOTree;
-import com.android.server.wifi.hotspot2.omadm.OMAParser;
-import com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager;
-import com.android.server.wifi.hotspot2.omadm.XMLNode;
-import com.android.server.wifi.hotspot2.pps.Credential;
-import com.android.server.wifi.hotspot2.pps.HomeSP;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.xml.sax.SAXException;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Unit tests for {@link com.android.server.wifi.hotspot2.omadm.PasspointManagementObjectManager}.
- */
-@SmallTest
-public class PasspointManagementObjectManagerTest {
-
- private static final String TAG = "PasspointManagementObjectManagerTest";
-
- @Rule
- public TemporaryFolder tempFolder = new TemporaryFolder();
-
- private File createFileFromResource(String configFile) throws Exception {
- InputStream in = getClass().getClassLoader().getResourceAsStream(configFile);
- File file = tempFolder.newFile(configFile.split("/")[1]);
-
- BufferedReader reader = new BufferedReader(new InputStreamReader(in));
- FileOutputStream out = new FileOutputStream(file);
-
- String line;
-
- while ((line = reader.readLine()) != null) {
- out.write(line.getBytes(StandardCharsets.UTF_8));
- }
-
- out.flush();
- out.close();
- return file;
- }
-
- private void addMoFromWifiConfig(PasspointManagementObjectManager moMgr) throws Exception {
- HashMap<String, Long> ssidMap = new HashMap<>();
- String fqdn = "tunisia.org";
- HashSet<Long> roamingConsortiums = new HashSet<Long>();
- HashSet<String> otherHomePartners = new HashSet<String>();
- Set<Long> matchAnyOIs = new HashSet<Long>();
- List<Long> matchAllOIs = new ArrayList<Long>();
- String friendlyName = "Tunisian Passpoint Provider";
- String iconUrl = "http://www.tunisia.org/icons/base_icon.png";
-
- WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
- enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
- enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.PAP);
- enterpriseConfig.setIdentity("testIdentity1");
- enterpriseConfig.setPassword("AnDrO1D");
-
- KeyStore keyStore = mock(KeyStore.class);
- Credential credential = new Credential(enterpriseConfig, keyStore, false);
-
- HomeSP newHomeSP = new HomeSP(Collections.<String, Long>emptyMap(), fqdn,
- roamingConsortiums, Collections.<String>emptySet(),
- Collections.<Long>emptySet(), Collections.<Long>emptyList(),
- friendlyName, null, credential);
-
- moMgr.addSP(newHomeSP);
- }
-
- private void addMoFromXml(PasspointManagementObjectManager moMgr) throws Exception {
- InputStream in = getClass().getClassLoader().getResourceAsStream(R2_TTLS_XML_FILE);
- BufferedReader reader = new BufferedReader(new InputStreamReader(in));
- StringBuilder builder = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- builder.append(line).append("\n");
- }
-
- String xml = builder.toString();
- moMgr.addSP(xml);
- }
-
- private static final String R1_CONFIG_FILE = "assets/r1.PerProviderSubscription.conf";
- private static final String R2_CONFIG_FILE = "assets/r2.PerProviderSubscription.conf";
- private static final String R2_TTLS_XML_FILE = "assets/r2-ttls-tree.xml";
- private static final String RUCKUS_CONFIG_FILE = "assets/ruckus.PerProviderSubscription.conf";
-
- /** ensure that loading R1 configs works */
- @Test
- public void loadR1Configs() throws Exception {
- File file = createFileFromResource(R1_CONFIG_FILE);
- PasspointManagementObjectManager moMgr = new PasspointManagementObjectManager(file, true);
- List<HomeSP> homeSPs = moMgr.loadAllSPs();
- assertEquals(2, homeSPs.size());
-
- /* TODO: Verify more attributes */
- HomeSP homeSP = moMgr.getHomeSP("twcwifi.com");
- assertNotNull(homeSP);
- assertEquals("TWC-WiFi", homeSP.getFriendlyName());
- assertEquals("tushar4", homeSP.getCredential().getUserName());
-
- homeSP = moMgr.getHomeSP("wi-fi.org");
- assertNotNull(homeSP);
- assertEquals("Wi-Fi Alliance", homeSP.getFriendlyName());
- assertEquals("sta006", homeSP.getCredential().getUserName());
- }
-
- /** ensure that loading R2 configs works */
- @Test
- public void loadR2Configs() throws Exception {
- File file = createFileFromResource(R2_CONFIG_FILE);
- PasspointManagementObjectManager moMgr = new PasspointManagementObjectManager(file, true);
- List<HomeSP> homeSPs = moMgr.loadAllSPs();
- assertEquals(2, homeSPs.size());
-
- /* TODO: Verify more attributes */
- HomeSP homeSP = moMgr.getHomeSP("twcwifi.com");
- assertNotNull(homeSP);
- assertEquals("TWC-WiFi", homeSP.getFriendlyName());
- assertEquals("tushar4", homeSP.getCredential().getUserName());
-
- homeSP = moMgr.getHomeSP("wi-fi.org");
- assertNotNull(homeSP);
- assertEquals("Wi-Fi Alliance", homeSP.getFriendlyName());
- assertEquals("sta015", homeSP.getCredential().getUserName());
- }
-
- /** verify adding a new service provider works */
- @Test
- public void addSP() throws Exception {
- File file = tempFolder.newFile("PerProviderSubscription.conf");
- PasspointManagementObjectManager moMgr = new PasspointManagementObjectManager(file, true);
- List<HomeSP> homeSPs = moMgr.loadAllSPs();
- assertEquals(0, homeSPs.size());
-
- addMoFromWifiConfig(moMgr);
- addMoFromXml(moMgr);
-
- PasspointManagementObjectManager moMgr2 = new PasspointManagementObjectManager(file, true);
- homeSPs = moMgr2.loadAllSPs();
- assertEquals(2, homeSPs.size());
-
- /* TODO: Verify more attributes */
- HomeSP homeSP = moMgr2.getHomeSP("rk-ttls.org");
- assertNotNull(homeSP);
- assertEquals("RK TTLS", homeSP.getFriendlyName());
- assertEquals("sta020", homeSP.getCredential().getUserName());
-
- homeSP = moMgr.getHomeSP("tunisia.org");
- assertNotNull(homeSP);
- assertEquals("Tunisian Passpoint Provider", homeSP.getFriendlyName());
- assertEquals("testIdentity1", homeSP.getCredential().getUserName());
- }
-
- /** Verify IOException is thrown when trying to add a SP from a null XML string. */
- @Test(expected = IOException.class)
- public void addSPFromNullXmlString() throws Exception {
- File file = tempFolder.newFile("PerProviderSubscription.conf");
- PasspointManagementObjectManager moMgr = new PasspointManagementObjectManager(file, true);
- String xml = null; // Needed to avoid ambiguity on function call.
- moMgr.addSP(xml);
- }
-
- /** Verify IOException is thrown when trying to build a SP from a null XML string. */
- @Test(expected = IOException.class)
- public void buildSPFromNullXmlString() throws Exception {
- PasspointManagementObjectManager.buildSP(null);
- }
-
- /** verify that xml serialization/deserialization works */
- public void checkXml() throws Exception {
- InputStream in = getClass().getClassLoader().getResourceAsStream(R2_TTLS_XML_FILE);
- BufferedReader reader = new BufferedReader(new InputStreamReader(in));
- StringBuilder builder = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- builder.append(line).append("\n");
- }
-
- String xmlIn = builder.toString();
-
- try {
- // Parse the file content:
- OMAParser parser = new OMAParser();
- MOTree moTree = parser.parse(xmlIn, "");
- XMLNode rootIn = parser.getRoot();
-
- // Serialize it back out:
- String xmlOut = moTree.toXml();
- parser = new OMAParser();
- // And parse it again:
- parser.parse(xmlOut, "");
- XMLNode rootOut = parser.getRoot();
-
- // Compare the two roots:
- assertTrue("Checking serialized XML", rootIn.equals(rootOut));
- } catch (SAXException se) {
- throw new IOException(se);
- }
- }
-
- /** verify modifying an existing service provider works */
- @Test
- public void modifySP() throws Exception {
- File file = createFileFromResource(R2_CONFIG_FILE);
- PasspointManagementObjectManager moMgr = new PasspointManagementObjectManager(file, true);
- List<HomeSP> homeSPs = moMgr.loadAllSPs();
- assertEquals(2, homeSPs.size());
-
- /* verify that wi-fi.org has update identifier of 1 */
- HomeSP homeSP = moMgr.getHomeSP("wi-fi.org");
- assertNotNull(homeSP);
- assertEquals("Wi-Fi Alliance", homeSP.getFriendlyName());
- assertEquals("sta015", homeSP.getCredential().getUserName());
- assertEquals(1, homeSP.getUpdateIdentifier());
-
- /* PasspointManagementObjectDefinition to change update identifier */
- String urn = "wfa:mo:hotspot2dot0-perprovidersubscription:1.0";
- String baseUri = "./Wi-Fi/wi-fi.org/PerProviderSubscription/UpdateIdentifier";
- String xmlTree =
- "<MgmtTree>\n"
- + " <VerDTD>1.2</VerDTD>\n"
- + " <Node>\n"
- + " <NodeName>UpdateIdentifier</NodeName>\n"
- + " <Value>9</Value>\n"
- + " </Node>\n"
- + "</MgmtTree>";
-
- PasspointManagementObjectDefinition moDef =
- new PasspointManagementObjectDefinition(baseUri, urn, xmlTree);
- List<PasspointManagementObjectDefinition> moDefs =
- new ArrayList<PasspointManagementObjectDefinition>();
- moDefs.add(moDef);
- moMgr.modifySP("wi-fi.org", moDefs);
-
- /* reload all the SPs again */
- moMgr.loadAllSPs();
-
- homeSP = moMgr.getHomeSP("wi-fi.org");
- assertEquals("Wi-Fi Alliance", homeSP.getFriendlyName());
- assertEquals("sta015", homeSP.getCredential().getUserName());
- assertEquals(9, homeSP.getUpdateIdentifier());
- }
-
- /** Verify IOException is thrown when trying to modify a SP using a null XML string. */
- @Test(expected = IOException.class)
- public void modifySPFromNullXmlString() throws Exception {
- File file = createFileFromResource(R2_CONFIG_FILE);
- PasspointManagementObjectManager moMgr = new PasspointManagementObjectManager(file, true);
- List<HomeSP> homeSPs = moMgr.loadAllSPs();
- assertEquals(2, homeSPs.size());
-
- /* PasspointManagementObjectDefinition with null xmlTree. */
- String urn = "wfa:mo:hotspot2dot0-perprovidersubscription:1.0";
- String baseUri = "./Wi-Fi/wi-fi.org/PerProviderSubscription/UpdateIdentifier";
- String xmlTree = null;
-
- PasspointManagementObjectDefinition moDef =
- new PasspointManagementObjectDefinition(baseUri, urn, xmlTree);
- List<PasspointManagementObjectDefinition> moDefs =
- new ArrayList<PasspointManagementObjectDefinition>();
- moDefs.add(moDef);
- moMgr.modifySP("wi-fi.org", moDefs);
- }
-
- /** verify removing an existing service provider works */
- @Test
- public void removeSP() throws Exception {
- File file = createFileFromResource(R2_CONFIG_FILE);
- PasspointManagementObjectManager moMgr = new PasspointManagementObjectManager(file, true);
- List<HomeSP> homeSPs = moMgr.loadAllSPs();
- assertEquals(2, homeSPs.size());
-
- moMgr.removeSP("wi-fi.org");
-
- homeSPs = moMgr.loadAllSPs();
- assertEquals(null, moMgr.getHomeSP("wi-fi.org"));
- }
-}
-
-
-
-
-
-
-
-
-
diff --git a/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java b/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java
index 177705c..689ade8 100644
--- a/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/RttServiceTest.java
@@ -30,14 +30,18 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;
+import android.net.wifi.IWificond;
import android.net.wifi.RttManager;
import android.net.wifi.RttManager.ParcelableRttParams;
import android.net.wifi.RttManager.ResponderConfig;
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 +64,11 @@
Context mContext;
@Mock
WifiNative mWifiNative;
- MockLooper mLooper;
+ TestLooper mLooper;
+ @Mock
+ WifiInjector mWifiInjector;
+ @Mock
+ IWificond mWificond;
RttService.RttServiceImpl mRttServiceImpl;
ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = ArgumentCaptor
@@ -69,9 +77,11 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- TestUtil.installWlanWifiNative(mWifiNative);
- mLooper = new MockLooper();
- mRttServiceImpl = new RttService.RttServiceImpl(mContext, mLooper.getLooper());
+ mLooper = new TestLooper();
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
+ mRttServiceImpl = new RttService.RttServiceImpl(mContext, mLooper.getLooper(),
+ mWifiInjector);
mRttServiceImpl.startService();
}
diff --git a/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java
new file mode 100644
index 0000000..7f63604
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/SavedNetworkEvaluatorTest.java
@@ -0,0 +1,548 @@
+/*
+ * 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.WifiConfigurationTestUtil.SECURITY_NONE;
+import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.LocalLog;
+
+import com.android.internal.R;
+import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.SavedNetworkEvaluator}.
+ */
+@SmallTest
+public class SavedNetworkEvaluatorTest {
+
+ /** Sets up test. */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ setupContext();
+ setupResource();
+ setupWifiConfigManager();
+ mLocalLog = new LocalLog(512);
+
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(false);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
+
+ mThresholdMinimumRssi2G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
+ mThresholdMinimumRssi5G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
+ mThresholdQualifiedRssi2G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
+ mThresholdQualifiedRssi5G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
+ mThresholdSaturatedRssi2G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz);
+ mThresholdSaturatedRssi5G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz);
+
+ mSavedNetworkEvaluator = new SavedNetworkEvaluator(mContext, mWifiConfigManager,
+ mClock, mLocalLog, mWifiConnectivityHelper);
+ }
+
+ /** Cleans up test. */
+ @After
+ public void cleanup() {
+ validateMockitoUsage();
+ }
+
+ private SavedNetworkEvaluator mSavedNetworkEvaluator;
+ @Mock private WifiConfigManager mWifiConfigManager;
+ @Mock private WifiConnectivityHelper mWifiConnectivityHelper;
+ @Mock private Context mContext;
+ @Mock private Resources mResource;
+ @Mock private Clock mClock;
+ private LocalLog mLocalLog;
+ private int mThresholdMinimumRssi2G;
+ private int mThresholdMinimumRssi5G;
+ private int mThresholdQualifiedRssi2G;
+ private int mThresholdQualifiedRssi5G;
+ private int mThresholdSaturatedRssi2G;
+ private int mThresholdSaturatedRssi5G;
+
+ private void setupContext() {
+ when(mContext.getResources()).thenReturn(mResource);
+ }
+
+ private void setupResource() {
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz))
+ .thenReturn(-57);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz))
+ .thenReturn(-60);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz))
+ .thenReturn(-70);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz))
+ .thenReturn(-73);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz))
+ .thenReturn(-82);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz))
+ .thenReturn(-85);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_RSSI_SCORE_SLOPE))
+ .thenReturn(4);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_RSSI_SCORE_OFFSET))
+ .thenReturn(85);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_SAME_BSSID_AWARD))
+ .thenReturn(24);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_SECURITY_AWARD))
+ .thenReturn(80);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_5GHz_preference_boost_factor))
+ .thenReturn(16);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_current_network_boost))
+ .thenReturn(16);
+ }
+
+ private void setupWifiConfigManager() {
+ when(mWifiConfigManager.getLastSelectedNetwork())
+ .thenReturn(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+
+ /**
+ * Do not evaluate networks that {@link WifiConfiguration#useExternalScores}.
+ */
+ @Test
+ public void ignoreNetworksIfUseExternalScores() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ for (WifiConfiguration wifiConfiguration : savedConfigs) {
+ wifiConfiguration.useExternalScores = true;
+ }
+
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, true, false, null);
+
+ assertNull(candidate);
+ }
+
+ /**
+ * Do not evaluate networks that {@link WifiConfiguration#isEphemeral}.
+ */
+ @Test
+ public void ignoreEphemeralNetworks() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[ESS]", "[ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10};
+ int[] securities = {SECURITY_NONE, SECURITY_NONE};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ for (WifiConfiguration wifiConfiguration : savedConfigs) {
+ wifiConfiguration.ephemeral = true;
+ }
+
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, true, false, null);
+
+ assertNull(candidate);
+ }
+
+ /**
+ * Set the candidate {@link ScanResult} for all {@link WifiConfiguration}s regardless of
+ * whether they are secure saved, open saved, or {@link WifiConfiguration#useExternalScores}.
+ */
+ @Test
+ public void setCandidateScanResultsForAllSavedNetworks() {
+ String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"};
+ int[] freqs = {5200, 5220, 5240};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels =
+ {mThresholdQualifiedRssi5G, mThresholdQualifiedRssi5G, mThresholdQualifiedRssi5G};
+ int[] securities = {SECURITY_PSK, SECURITY_NONE, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration useExternalScoresConfig = scanDetailsAndConfigs.getWifiConfigs()[0];
+ useExternalScoresConfig.useExternalScores = true;
+ WifiConfiguration openNetworkConfig = scanDetailsAndConfigs.getWifiConfigs()[1];
+ WifiConfiguration secureNetworkConfig = scanDetailsAndConfigs.getWifiConfigs()[2];
+
+ mSavedNetworkEvaluator.evaluateNetworks(scanDetails, null, null, true, false, null);
+
+ verify(mWifiConfigManager, atLeastOnce()).setNetworkCandidateScanResult(
+ eq(useExternalScoresConfig.networkId),
+ eq(scanDetails.get(0).getScanResult()),
+ anyInt());
+ verify(mWifiConfigManager, atLeastOnce()).setNetworkCandidateScanResult(
+ eq(openNetworkConfig.networkId),
+ eq(scanDetails.get(1).getScanResult()),
+ anyInt());
+ verify(mWifiConfigManager, atLeastOnce()).setNetworkCandidateScanResult(
+ eq(secureNetworkConfig.networkId),
+ eq(scanDetails.get(2).getScanResult()),
+ anyInt());
+ }
+
+ /**
+ * Between two 2G networks, choose the one with stronger RSSI value if other conditions
+ * are the same and the RSSI values are not saturated.
+ */
+ @Test
+ public void chooseStrongerRssi2GNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, true, false, null);
+
+ ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+ /**
+ * Between two 5G networks, choose the one with stronger RSSI value if other conditions
+ * are the same and the RSSI values are not saturated.
+ */
+ @Test
+ public void chooseStrongerRssi5GNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {5200, 5240};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi5G + 8, mThresholdQualifiedRssi5G + 10};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, true, false, null);
+
+ ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+ /**
+ * Choose secure network over open network if other conditions are the same.
+ */
+ @Test
+ public void chooseSecureNetworkOverOpenNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {5200, 5240};
+ String[] caps = {"[ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi5G, mThresholdQualifiedRssi5G};
+ int[] securities = {SECURITY_NONE, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, true, false, null);
+
+ ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+ /**
+ * Choose 5G network over 2G network if other conditions are the same.
+ */
+ @Test
+ public void choose5GNetworkOver2GNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 5240};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G, mThresholdQualifiedRssi5G};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, true, false, null);
+
+ ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+ /**
+ * Verify that we stick to the currently connected network if the other one is
+ * just slightly better scored.
+ */
+ @Test
+ public void stickToCurrentNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {5200, 5240};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ // test2 has slightly stronger RSSI value than test1
+ int[] levels = {mThresholdMinimumRssi5G + 2, mThresholdMinimumRssi5G + 4};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ // Simuluate we are connected to SSID test1 already.
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ savedConfigs[0], null, true, false, null);
+
+ // Even though test2 has higher RSSI value, test1 is chosen because of the
+ // currently connected network bonus.
+ ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+ /**
+ * Verify that we stick to the currently connected BSSID if the other one is
+ * just slightly better scored.
+ */
+ @Test
+ public void stickToCurrentBSSID() {
+ // Same SSID
+ String[] ssids = {"\"test1\"", "\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {5200, 5240};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ // test2 has slightly stronger RSSI value than test1
+ int[] levels = {mThresholdMinimumRssi5G + 2, mThresholdMinimumRssi5G + 6};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ // Simuluate we are connected to BSSID "6c:f3:7f:ae:8c:f3" already
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, bssids[0], true, false, null);
+
+ // Even though test2 has higher RSSI value, test1 is chosen because of the
+ // currently connected BSSID bonus.
+ ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+ }
+
+ /**
+ * Verify that the same BSSID award is applied to all the BSSIDs which are under the same
+ * network as the currently connected BSSID.
+ */
+ @Test
+ public void currentBssidAwardForAllBssidsWithinTheSameNetworkWhenFirmwareRoamingSupported() {
+ // Three BSSIDs are carefully setup there:
+ // BSSID_0 and BSSID_1 have the same SSID and security type, so they are considered under
+ // the same 2.4 GHz network. BSSID_1 RSSI is stronger than BSSID_0.
+ // BSSID_2 is under a 5GHz network different from BSSID_0 and BSSID_1. Its RSSI is
+ // slightly stronger than BSSID_1.
+ //
+ // When firmware roaming is not supported, BSSID_2 has higher score than BSSID_0 and
+ // BSSID_1.
+ // When firmware roaming is suported, BSSID_1 has higher score than BSSID_2 because the
+ // same BSSID award is now applied to both BSSID_0 and BSSID_1.
+ String[] ssids = {"\"test1\"", "\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"};
+ int[] freqs = {2470, 2437, 5200};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 2, mThresholdMinimumRssi2G + 5,
+ mThresholdMinimumRssi5G + 7};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ // Firmware roaming is not supported.
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(false);
+ // Simuluate we are connected to BSSID_0 already.
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ savedConfigs[0], bssids[0], true, false, null);
+ // Verify that BSSID_2 is chosen.
+ ScanResult chosenScanResult = scanDetails.get(2).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[2], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+
+ // Firmware roaming is supported.
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+ // Simuluate we are connected to BSSID_0 already.
+ candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ savedConfigs[0], bssids[0], true, false, null);
+ // Verify that BSSID_1 is chosen.
+ chosenScanResult = scanDetails.get(1).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+ /**
+ * One 2.4GHz network and one 5GHz network have the same security type. Perform
+ * the following tests to verify that once across the RSSI saturation threshold
+ * stronger RSSI value doesn't increase network score.
+ *
+ * 1) Both 2.4GHz network and 5GHz network have the same RSSI value,
+ * mThresholdQualifiedRssi2G, which is below the saturation threshold. 5GHz
+ * network is chosen because of the 5G band award.
+ * 2) Bump up 2.4GHz network RSSI 20dBm higher. Verify that it helps the 2.4GHz network
+ * score and it gets chosen over the 5GHz network.
+ * 3) Bring both 2.4GHz network and 5GHz network RSSI value to mThresholdSaturatedRssi2G.
+ * Verify that 5GHz network is chosen because of the 5G band award.
+ * 4) Bump up 2.4GHz network RSSI to be 20dBm higher than mThresholdSaturatedRssi2G.
+ * Verify that the incresed RSSI doesn't help 2.4GHz network score and 5GHz network
+ * is still chosen.
+ */
+ @Test
+ public void saturatedRssiAddsNoWeightToNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 5400};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ // 1) The RSSI of both networks is mThresholdQualifiedRssi2G
+ int[] levels = {mThresholdQualifiedRssi2G, mThresholdQualifiedRssi2G};
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ WifiConfiguration candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, false, false, null);
+ // Verify that 5GHz network is chosen because of 5G band award
+ ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+
+ // 2) Bump up 2.4GHz network RSSI by 20dBm.
+ levels[0] = mThresholdQualifiedRssi2G + 20;
+ scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids,
+ bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ scanDetails = scanDetailsAndConfigs.getScanDetails();
+ savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, null, null, false,
+ false, null);
+ // Verify that 2.4GHz network is chosen because of much higher RSSI value
+ chosenScanResult = scanDetails.get(0).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+
+ // 3) Bring both 2.4GHz network and 5GHz network RSSI to mThresholdSaturatedRssi2G
+ levels[0] = levels[1] = mThresholdSaturatedRssi2G;
+ scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids,
+ bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ scanDetails = scanDetailsAndConfigs.getScanDetails();
+ savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, null, null, false,
+ false, null);
+ // Verify that 5GHz network is chosen because of 5G band award
+ chosenScanResult = scanDetails.get(1).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+
+ // 4) Bump 2.4GHz network RSSI to be 20dBm higher than mThresholdSaturatedRssi2G
+ levels[0] = mThresholdSaturatedRssi2G + 20;
+ scanDetailsAndConfigs = WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids,
+ bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ scanDetails = scanDetailsAndConfigs.getScanDetails();
+ savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ candidate = mSavedNetworkEvaluator.evaluateNetworks(scanDetails, null, null, false,
+ false, null);
+ // Verify that the increased RSSI doesn't help 2.4GHz network and 5GHz network
+ // is still chosen
+ chosenScanResult = scanDetails.get(1).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java
index 7a1bdd2..ab9a762 100644
--- a/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/ScanTestUtil.java
@@ -111,12 +111,16 @@
}
/**
- * Add the provided hidden network IDs to scan request.
- * @param networkIds List of hidden network IDs
+ * Add the provided hidden network SSIDs to scan request.
+ * @param networkSSIDs List of hidden network SSIDs
* @return builder object
*/
- public NativeScanSettingsBuilder withHiddenNetworkIds(int[] networkIds) {
- mSettings.hiddenNetworkIds = networkIds;
+ public NativeScanSettingsBuilder withHiddenNetworkSSIDs(String[] networkSSIDs) {
+ mSettings.hiddenNetworks = new WifiNative.HiddenNetwork[networkSSIDs.length];
+ for (int i = 0; i < networkSSIDs.length; i++) {
+ mSettings.hiddenNetworks[i] = new WifiNative.HiddenNetwork();
+ mSettings.hiddenNetworks[i].ssid = networkSSIDs[i];
+ }
return this;
}
@@ -233,6 +237,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 +257,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) {
@@ -367,10 +375,6 @@
for (int i = 0; i < expected.networkList.length; i++) {
assertEquals("networkList[" + i + "].ssid",
expected.networkList[i].ssid, actual.networkList[i].ssid);
- assertEquals("networkList[" + i + "].networkId",
- expected.networkList[i].networkId, actual.networkList[i].networkId);
- assertEquals("networkList[" + i + "].priority",
- expected.networkList[i].priority, actual.networkList[i].priority);
assertEquals("networkList[" + i + "].flags",
expected.networkList[i].flags, actual.networkList[i].flags);
assertEquals("networkList[" + i + "].auth_bit_field",
diff --git a/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java
new file mode 100644
index 0000000..55b00c9
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/ScoredNetworkEvaluatorTest.java
@@ -0,0 +1,577 @@
+/*
+ * 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.WifiConfigurationTestUtil.SECURITY_NONE;
+import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.NetworkKey;
+import android.net.NetworkScoreManager;
+import android.net.Uri;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiNetworkScoreCache;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.LocalLog;
+
+import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link ScoredNetworkEvaluator}.
+ */
+@SmallTest
+public class ScoredNetworkEvaluatorTest {
+ private ContentObserver mContentObserver;
+ private int mThresholdQualifiedRssi2G;
+ private int mThresholdQualifiedRssi5G;
+
+ @Mock private Context mContext;
+ @Mock private Clock mClock;
+ @Mock private FrameworkFacade mFrameworkFacade;
+ @Mock private NetworkScoreManager mNetworkScoreManager;
+ @Mock private WifiConfigManager mWifiConfigManager;
+
+ @Captor private ArgumentCaptor<NetworkKey[]> mNetworkKeyArrayCaptor;
+
+ private WifiNetworkScoreCache mScoreCache;
+ private ScoredNetworkEvaluator mScoredNetworkEvaluator;
+
+ @Before
+ public void setUp() throws Exception {
+ mThresholdQualifiedRssi2G = -73;
+ mThresholdQualifiedRssi5G = -70;
+
+ MockitoAnnotations.initMocks(this);
+
+ when(mFrameworkFacade.getIntegerSetting(mContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0))
+ .thenReturn(1);
+
+ ArgumentCaptor<ContentObserver> observerCaptor =
+ ArgumentCaptor.forClass(ContentObserver.class);
+ mScoreCache = new WifiNetworkScoreCache(mContext);
+ mScoredNetworkEvaluator = new ScoredNetworkEvaluator(mContext,
+ Looper.getMainLooper(), mFrameworkFacade, mNetworkScoreManager,
+ mWifiConfigManager, new LocalLog(0), mScoreCache);
+ verify(mFrameworkFacade).registerContentObserver(eq(mContext), any(Uri.class), eq(false),
+ observerCaptor.capture());
+ mContentObserver = observerCaptor.getValue();
+
+ reset(mNetworkScoreManager);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
+ }
+
+ @After
+ public void tearDown() {
+ validateMockitoUsage();
+ }
+
+ @Test
+ public void testUpdate_recommendationsDisabled() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {2470};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 8};
+ int[] securities = {SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil
+ .setupScanDetailsAndConfigStore(
+ ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock);
+
+ when(mFrameworkFacade.getIntegerSetting(mContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0))
+ .thenReturn(0);
+
+ mContentObserver.onChange(false /* unused */);
+
+ mScoredNetworkEvaluator.update(scanDetailsAndConfigs.getScanDetails());
+
+ verifyZeroInteractions(mNetworkScoreManager);
+ }
+
+ @Test
+ public void testUpdate_emptyScanList() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {2470};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 8};
+ int[] securities = {SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil
+ .setupScanDetailsAndConfigStore(
+ ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock);
+
+ mScoredNetworkEvaluator.update(new ArrayList<ScanDetail>());
+
+ verifyZeroInteractions(mNetworkScoreManager);
+ }
+
+ @Test
+ public void testUpdate_allNetworksUnscored() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
+ int[] securities = {SECURITY_PSK, SECURITY_NONE};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil
+ .setupScanDetailsAndConfigStore(
+ ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock);
+
+ mScoredNetworkEvaluator.update(scanDetailsAndConfigs.getScanDetails());
+
+ verify(mNetworkScoreManager).requestScores(mNetworkKeyArrayCaptor.capture());
+ assertEquals(2, mNetworkKeyArrayCaptor.getValue().length);
+ NetworkKey expectedNetworkKey = NetworkKey.createFromScanResult(
+ scanDetailsAndConfigs.getScanDetails().get(0).getScanResult());
+ assertEquals(expectedNetworkKey, mNetworkKeyArrayCaptor.getValue()[0]);
+ expectedNetworkKey = NetworkKey.createFromScanResult(
+ scanDetailsAndConfigs.getScanDetails().get(1).getScanResult());
+ assertEquals(expectedNetworkKey, mNetworkKeyArrayCaptor.getValue()[1]);
+ }
+
+ @Test
+ public void testUpdate_oneScored_oneUnscored() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
+ int[] securities = {SECURITY_PSK, SECURITY_NONE};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs = WifiNetworkSelectorTestUtil
+ .setupScanDetailsAndConfigStore(
+ ssids, bssids, freqs, caps, levels, securities, mWifiConfigManager, mClock);
+
+ List<ScanDetail> scoredScanDetails = scanDetailsAndConfigs.getScanDetails().subList(0, 1);
+ Integer[] scores = {120};
+ boolean[] meteredHints = {true};
+ WifiNetworkSelectorTestUtil.configureScoreCache(
+ mScoreCache, scoredScanDetails, scores, meteredHints);
+
+ mScoredNetworkEvaluator.update(scanDetailsAndConfigs.getScanDetails());
+
+ verify(mNetworkScoreManager).requestScores(mNetworkKeyArrayCaptor.capture());
+
+ NetworkKey[] requestedScores = mNetworkKeyArrayCaptor.getValue();
+ assertEquals(1, requestedScores.length);
+ NetworkKey expectedNetworkKey = NetworkKey.createFromScanResult(
+ scanDetailsAndConfigs.getScanDetails().get(1).getScanResult());
+ assertEquals(expectedNetworkKey, requestedScores[0]);
+ }
+
+ @Test
+ public void testEvaluateNetworks_recommendationsDisabled() {
+ when(mFrameworkFacade.getIntegerSetting(mContext,
+ Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0))
+ .thenReturn(0);
+
+ mContentObserver.onChange(false /* unused */);
+
+ mScoredNetworkEvaluator.evaluateNetworks(null, null, null, false, false, null);
+
+ verifyZeroInteractions(mWifiConfigManager, mNetworkScoreManager);
+ }
+
+ /**
+ * When no saved networks available, choose the available ephemeral networks
+ * if untrusted networks are allowed.
+ */
+ @Test
+ public void testEvaluateNetworks_chooseEphemeralNetworkBecauseOfNoSavedNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10};
+ Integer[] scores = {null, 120};
+ boolean[] meteredHints = {false, true};
+
+ List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+ ssids, bssids, freqs, caps, levels, mClock);
+ WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+ scanDetails, scores, meteredHints);
+
+ // No saved networks.
+ when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class)))
+ .thenReturn(null);
+
+ ScanResult scanResult = scanDetails.get(1).getScanResult();
+ WifiConfiguration ephemeralNetworkConfig = WifiNetworkSelectorTestUtil
+ .setupEphemeralNetwork(mWifiConfigManager, 1, scanDetails.get(1), meteredHints[1]);
+
+ // Untrusted networks allowed.
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, false, true, null);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(ephemeralNetworkConfig, candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ scanResult, candidate);
+ assertEquals(meteredHints[1], candidate.meteredHint);
+ }
+
+ /**
+ * When no saved networks available, choose the highest scored ephemeral networks
+ * if untrusted networks are allowed.
+ */
+ @Test
+ public void testEvaluateNetworks_chooseHigherScoredEphemeralNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[ESS]", "[ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 8};
+ Integer[] scores = {100, 120};
+ boolean[] meteredHints = {true, true};
+ ScanResult[] scanResults = new ScanResult[2];
+ WifiConfiguration[] ephemeralNetworkConfigs = new WifiConfiguration[2];
+
+ List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+ ssids, bssids, freqs, caps, levels, mClock);
+ WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+ scanDetails, scores, meteredHints);
+
+ // No saved networks.
+ when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class)))
+ .thenReturn(null);
+
+ for (int i = 0; i < 2; i++) {
+ scanResults[i] = scanDetails.get(i).getScanResult();
+ ephemeralNetworkConfigs[i] = WifiNetworkSelectorTestUtil.setupEphemeralNetwork(
+ mWifiConfigManager, i, scanDetails.get(i), meteredHints[i]);
+ }
+
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, false, true, null);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(ephemeralNetworkConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ scanResults[1], candidate);
+ assertEquals(meteredHints[1], candidate.meteredHint);
+ }
+
+ /**
+ * Don't choose available ephemeral networks if no saved networks and untrusted networks
+ * are not allowed.
+ */
+ @Test
+ public void testEvaluateNetworks_noEphemeralNetworkWhenUntrustedNetworksNotAllowed() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 10};
+ Integer[] scores = {null, 120};
+ boolean[] meteredHints = {false, true};
+
+ List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(
+ ssids, bssids, freqs, caps, levels, mClock);
+ WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+ scanDetails, scores, meteredHints);
+
+ // No saved networks.
+ when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class)))
+ .thenReturn(null);
+
+ WifiNetworkSelectorTestUtil.setupEphemeralNetwork(
+ mWifiConfigManager, 1, scanDetails.get(1), meteredHints[1]);
+
+ // Untrusted networks not allowed.
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, false, false, null);
+
+ assertEquals("Expect null configuration", null, candidate);
+ }
+
+ /**
+ * Choose externally scored saved network.
+ */
+ @Test
+ public void testEvaluateNetworks_chooseSavedNetworkWithExternalScore() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {5200};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
+ int[] securities = {SECURITY_PSK};
+ int[] levels = {mThresholdQualifiedRssi5G + 8};
+ Integer[] scores = {120};
+ boolean[] meteredHints = {false};
+
+ WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ savedConfigs[0].useExternalScores = true;
+
+ WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+ scanDetails, scores, meteredHints);
+
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, false, true, null);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ scanDetails.get(0).getScanResult(), candidate);
+ }
+
+ /**
+ * Choose externally scored saved network with higher score.
+ */
+ @Test
+ public void testEvaluateNetworks_chooseSavedNetworkWithHigherExternalScore() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 8};
+ Integer[] scores = {100, 120};
+ boolean[] meteredHints = {false, false};
+
+ WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ savedConfigs[0].useExternalScores = savedConfigs[1].useExternalScores = true;
+
+ WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+ scanDetails, scores, meteredHints);
+
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, false, true, null);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ scanDetails.get(1).getScanResult(), candidate);
+ }
+
+ /**
+ * Prefer externally scored saved network over untrusted network when they have
+ * the same score.
+ */
+ @Test
+ public void testEvaluateNetworks_chooseExternallyScoredOverUntrustedNetworksWithSameScore() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
+ int[] securities = {SECURITY_PSK, SECURITY_NONE};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 8};
+ Integer[] scores = {120, 120};
+ boolean[] meteredHints = {false, true};
+
+ WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ savedConfigs[0].useExternalScores = true;
+
+ WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+ scanDetails, scores, meteredHints);
+
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, false, true, null);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ scanDetails.get(0).getScanResult(), candidate);
+ }
+
+ /**
+ * Choose untrusted network when it has higher score than the externally scored
+ * saved network.
+ */
+ @Test
+ public void testEvaluateNetworks_chooseUntrustedWithHigherScoreThanExternallyScoredNetwork() {
+ // Saved network.
+ String[] savedSsids = {"\"test1\""};
+ String[] savedBssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] savedFreqs = {2470};
+ String[] savedCaps = {"[WPA2-EAP-CCMP][ESS]"};
+ int[] savedSecurities = {SECURITY_PSK};
+ int[] savedLevels = {mThresholdQualifiedRssi2G + 8};
+ // Ephemeral network.
+ String[] ephemeralSsids = {"\"test2\""};
+ String[] ephemeralBssids = {"6c:f3:7f:ae:8c:f4"};
+ int[] ephemeralFreqs = {2437};
+ String[] ephemeralCaps = {"[ESS]"};
+ int[] ephemeralLevels = {mThresholdQualifiedRssi2G + 8};
+ // Ephemeral network has higher score than the saved network.
+ Integer[] scores = {100, 120};
+ boolean[] meteredHints = {false, true};
+
+ // Set up the saved network.
+ WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(savedSsids,
+ savedBssids, savedFreqs, savedCaps, savedLevels, savedSecurities,
+ mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ savedConfigs[0].useExternalScores = true;
+
+ // Set up the ephemeral network.
+ scanDetails.addAll(WifiNetworkSelectorTestUtil.buildScanDetails(
+ ephemeralSsids, ephemeralBssids, ephemeralFreqs,
+ ephemeralCaps, ephemeralLevels, mClock));
+ ScanResult ephemeralScanResult = scanDetails.get(1).getScanResult();
+ WifiConfiguration ephemeralNetworkConfig = WifiNetworkSelectorTestUtil
+ .setupEphemeralNetwork(mWifiConfigManager, 1, scanDetails.get(1),
+ meteredHints[1]);
+
+ // Set up score cache for both the saved network and the ephemeral network.
+ WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+ scanDetails, scores, meteredHints);
+
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, false, true, null);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(ephemeralNetworkConfig, candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ ephemeralScanResult, candidate);
+ }
+
+ /**
+ * Prefer externally scored saved network over untrusted network when they have
+ * the same score.
+ */
+ @Test
+ public void testEvaluateNetworks_nullScoredNetworks() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
+ int[] securities = {SECURITY_PSK, SECURITY_NONE};
+ int[] levels = {mThresholdQualifiedRssi2G + 8, mThresholdQualifiedRssi2G + 8};
+ Integer[] scores = {null, null};
+ boolean[] meteredHints = {false, true};
+
+ WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ savedConfigs[0].useExternalScores = true;
+
+ WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+ scanDetails, scores, meteredHints);
+
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+ null, null, false, true, null);
+
+ assertEquals("Expect null configuration", null, candidate);
+ }
+
+ /**
+ * Between two ephemeral networks with the same RSSI, choose
+ * the currently connected one.
+ */
+ @Test
+ public void testEvaluateNetworks_chooseActiveEphemeralNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[ESS]", "[ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 28, mThresholdQualifiedRssi2G + 28};
+ boolean[] meteredHints = {true, true};
+ ScanResult[] scanResults = new ScanResult[2];
+ WifiConfiguration[] ephemeralNetworkConfigs = new WifiConfiguration[2];
+
+ List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil
+ .buildScanDetails(ssids, bssids, freqs, caps, levels, mClock);
+
+ WifiNetworkSelectorTestUtil.configureScoreCache(
+ mScoreCache, scanDetails, null, meteredHints);
+
+ // No saved networks.
+ when(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(any(ScanDetail.class)))
+ .thenReturn(null);
+
+ for (int i = 0; i < 2; i++) {
+ scanResults[i] = scanDetails.get(i).getScanResult();
+ ephemeralNetworkConfigs[i] = WifiNetworkSelectorTestUtil.setupEphemeralNetwork(
+ mWifiConfigManager, i, scanDetails.get(i), meteredHints[i]);
+ }
+
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(
+ scanDetails, ephemeralNetworkConfigs[1],
+ bssids[1], true, true, null);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(ephemeralNetworkConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ scanResults[1], candidate);
+ assertEquals(meteredHints[1], candidate.meteredHint);
+ }
+
+ /**
+ * Between two externally scored saved networks with the same RSSI, choose
+ * the currently connected one.
+ */
+ @Test
+ public void testEvaluateNetworks_chooseActiveSavedNetwork() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2470, 2437};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+ int[] levels = {mThresholdQualifiedRssi2G + 28, mThresholdQualifiedRssi2G + 28};
+ boolean[] meteredHints = {false, false};
+
+ WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ savedConfigs[0].useExternalScores = savedConfigs[1].useExternalScores = true;
+
+ WifiNetworkSelectorTestUtil.configureScoreCache(mScoreCache,
+ scanDetails, null, meteredHints);
+
+ WifiConfiguration candidate = mScoredNetworkEvaluator.evaluateNetworks(scanDetails,
+ savedConfigs[1], bssids[1], true, true, null);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[1], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ scanDetails.get(1).getScanResult(), candidate);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java b/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java
new file mode 100644
index 0000000..0e55b72
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/SelfRecoveryTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.mockito.Mockito.*;
+import static org.mockito.MockitoAnnotations.*;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.SelfRecovery}.
+ */
+@SmallTest
+public class SelfRecoveryTest {
+ SelfRecovery mSelfRecovery;
+ @Mock WifiController mWifiController;
+
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ mSelfRecovery = new SelfRecovery(mWifiController);
+ }
+
+ /**
+ * Verifies that invocations of {@link SelfRecovery#trigger(int)} with valid reasons will send
+ * the restart message to {@link WifiController}.
+ */
+ @Test
+ public void testValidTriggerReasonsSendMessageToWifiController() {
+ mSelfRecovery.trigger(SelfRecovery.REASON_LAST_RESORT_WATCHDOG);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+
+ mSelfRecovery.trigger(SelfRecovery.REASON_HAL_CRASH);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+
+ mSelfRecovery.trigger(SelfRecovery.REASON_WIFICOND_CRASH);
+ verify(mWifiController).sendMessage(eq(WifiController.CMD_RESTART_WIFI));
+ reset(mWifiController);
+
+ }
+
+ /**
+ * Verifies that invocations of {@link SelfRecovery#trigger(int)} with invalid reasons will not
+ * send the restart message to {@link WifiController}.
+ */
+ @Test
+ public void testInvalidTriggerReasonsDoesNotSendMessageToWifiController() {
+ mSelfRecovery.trigger(-1);
+ verify(mWifiController, never()).sendMessage(anyInt());
+
+ mSelfRecovery.trigger(8);
+ verify(mWifiController, never()).sendMessage(anyInt());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index b91df5a..900e6a6 100644
--- a/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -17,6 +17,7 @@
package com.android.server.wifi;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
@@ -24,16 +25,18 @@
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 com.android.server.net.BaseNetworkObserver;
+
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -41,6 +44,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
@@ -51,21 +55,30 @@
private static final String TAG = "SoftApManagerTest";
- private static final String TEST_INTERFACE_NAME = "TestInterface";
+ private static final String DEFAULT_SSID = "DefaultTestSSID";
+ private static final String TEST_SSID = "TestSSID";
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 static final String TEST_INTERFACE_NAME = "testif0";
private final ArrayList<Integer> mAllowed2GChannels =
- new ArrayList<Integer>(Arrays.asList(ALLOWED_2G_CHANNELS));
+ new ArrayList<>(Arrays.asList(ALLOWED_2G_CHANNELS));
- MockLooper mLooper;
- @Mock Context mContext;
+ private final WifiConfiguration mDefaultApConfig = createDefaultApConfig();
+
+ TestLooper mLooper;
@Mock WifiNative mWifiNative;
- @Mock INetworkManagementService mNmService;
- @Mock ConnectivityManager mConnectivityManager;
@Mock SoftApManager.Listener mListener;
@Mock InterfaceConfiguration mInterfaceConfiguration;
+ @Mock IBinder mApInterfaceBinder;
+ @Mock IApInterface mApInterface;
+ @Mock INetworkManagementService mNmService;
+ @Mock WifiApConfigStore mWifiApConfigStore;
+ @Mock WifiMetrics mWifiMetrics;
+ final ArgumentCaptor<DeathRecipient> mDeathListenerCaptor =
+ ArgumentCaptor.forClass(DeathRecipient.class);
+ final ArgumentCaptor<BaseNetworkObserver> mNetworkObserverCaptor =
+ ArgumentCaptor.forClass(BaseNetworkObserver.class);
SoftApManager mSoftApManager;
@@ -73,40 +86,84 @@
@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);
-
- mSoftApManager = new SoftApManager(mLooper.getLooper(),
- mWifiNative,
- mNmService,
- TEST_COUNTRY_CODE,
- mAllowed2GChannels,
- mListener);
-
- mLooper.dispatchAll();
+ when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
+ when(mApInterface.startHostapd()).thenReturn(true);
+ when(mApInterface.stopHostapd()).thenReturn(true);
+ when(mApInterface.writeHostapdConfig(
+ any(), anyBoolean(), anyInt(), anyInt(), any())).thenReturn(true);
+ when(mApInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
}
- /** Verifies startSoftAp will fail if AP configuration is not provided. */
+ private WifiConfiguration createDefaultApConfig() {
+ WifiConfiguration defaultConfig = new WifiConfiguration();
+ defaultConfig.SSID = DEFAULT_SSID;
+ return defaultConfig;
+ }
+
+ private SoftApManager createSoftApManager(WifiConfiguration config) throws Exception {
+ when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
+ when(mApInterface.startHostapd()).thenReturn(true);
+ when(mApInterface.stopHostapd()).thenReturn(true);
+ if (config == null) {
+ when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig);
+ }
+ SoftApManager newSoftApManager = new SoftApManager(mLooper.getLooper(),
+ mWifiNative,
+ TEST_COUNTRY_CODE,
+ mListener,
+ mApInterface,
+ mNmService,
+ mWifiApConfigStore,
+ config,
+ mWifiMetrics);
+ mLooper.dispatchAll();
+ return newSoftApManager;
+ }
+
+ /** Verifies startSoftAp will use default config if AP configuration is not provided. */
@Test
public void startSoftApWithoutConfig() throws Exception {
- InOrder order = inOrder(mListener);
+ startSoftApAndVerifyEnabled(null);
+ }
- mSoftApManager.start(null);
+ /** Verifies startSoftAp will use provided config and start AP. */
+ @Test
+ public void startSoftApWithConfig() throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+ config.apBand = WifiConfiguration.AP_BAND_2GHZ;
+ config.SSID = TEST_SSID;
+ startSoftApAndVerifyEnabled(config);
+ }
+
+ /** Tests softap startup if default config fails to load. **/
+ @Test
+ public void startSoftApDefaultConfigFailedToLoad() throws Exception {
+ when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
+ when(mApInterface.startHostapd()).thenReturn(true);
+ when(mApInterface.stopHostapd()).thenReturn(true);
+ when(mWifiApConfigStore.getApConfiguration()).thenReturn(null);
+ SoftApManager newSoftApManager = new SoftApManager(mLooper.getLooper(),
+ mWifiNative,
+ TEST_COUNTRY_CODE,
+ mListener,
+ mApInterface,
+ mNmService,
+ mWifiApConfigStore,
+ null,
+ mWifiMetrics);
mLooper.dispatchAll();
-
- order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLING, 0);
- order.verify(mListener).onStateChanged(
- WifiManager.WIFI_AP_STATE_FAILED, WifiManager.SAP_START_FAILURE_GENERAL);
+ newSoftApManager.start();
+ mLooper.dispatchAll();
+ verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_FAILED,
+ WifiManager.SAP_START_FAILURE_GENERAL);
}
/** Tests the handling of stop command when soft AP is not started. */
@Test
public void stopWhenNotStarted() throws Exception {
+ mSoftApManager = createSoftApManager(null);
mSoftApManager.stop();
mLooper.dispatchAll();
/* Verify no state changes. */
@@ -116,36 +173,57 @@
/** Tests the handling of stop command when soft AP is started. */
@Test
public void stopWhenStarted() throws Exception {
- startSoftApAndVerifyEnabled();
+ startSoftApAndVerifyEnabled(null);
InOrder order = inOrder(mListener);
mSoftApManager.stop();
mLooper.dispatchAll();
- verify(mNmService).stopAccessPoint(TEST_INTERFACE_NAME);
+ verify(mApInterface).stopHostapd();
order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLING, 0);
order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_DISABLED, 0);
}
- /** Starts soft AP and verifies that it is enabled successfully. */
- protected void startSoftApAndVerifyEnabled() throws Exception {
- InOrder order = inOrder(mListener);
+ @Test
+ public void handlesWificondInterfaceDeath() throws Exception {
+ startSoftApAndVerifyEnabled(null);
- /**
- * Only test the default configuration. Testing for different configurations
- * are taken care of by ApConfigUtilTest.
- */
- WifiConfiguration config = new WifiConfiguration();
- config.apBand = WifiConfiguration.AP_BAND_2GHZ;
+ 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(WifiConfiguration config) throws Exception {
+ String expectedSSID;
+ InOrder order = inOrder(mListener, mApInterfaceBinder, mApInterface, mNmService);
+
when(mWifiNative.isHalStarted()).thenReturn(false);
when(mWifiNative.setCountryCodeHal(TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)))
.thenReturn(true);
- mSoftApManager.start(config);
+
+ mSoftApManager = createSoftApManager(config);
+ if (config == null) {
+ when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig);
+ expectedSSID = mDefaultApConfig.SSID;
+ } else {
+ expectedSSID = config.SSID;
+ }
+ mSoftApManager.start();
mLooper.dispatchAll();
- 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(mNmService).registerObserver(mNetworkObserverCaptor.capture());
+ order.verify(mApInterface).writeHostapdConfig(
+ eq(expectedSSID.getBytes(StandardCharsets.UTF_8)), anyBoolean(),
+ anyInt(), anyInt(), any());
+ order.verify(mApInterface).startHostapd();
+ mNetworkObserverCaptor.getValue().interfaceLinkStateChanged(TEST_INTERFACE_NAME, true);
+ mLooper.dispatchAll();
order.verify(mListener).onStateChanged(WifiManager.WIFI_AP_STATE_ENABLED, 0);
}
diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
new file mode 100644
index 0000000..2deef52
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java
@@ -0,0 +1,1580 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wifi;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyShort;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.test.MockAnswerUtil;
+import android.content.Context;
+import android.hardware.wifi.supplicant.V1_0.ISupplicant;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback.BssidChangeReason;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
+import android.hardware.wifi.supplicant.V1_0.IfaceType;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
+import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.net.IpConfiguration;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import com.android.server.wifi.hotspot2.AnqpEvent;
+import com.android.server.wifi.hotspot2.IconEvent;
+import com.android.server.wifi.hotspot2.WnmData;
+import com.android.server.wifi.util.NativeUtil;
+
+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.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * Unit tests for SupplicantStaIfaceHal
+ */
+public class SupplicantStaIfaceHalTest {
+ private static final String TAG = "SupplicantStaIfaceHalTest";
+ private static final Map<Integer, String> NETWORK_ID_TO_SSID = new HashMap<Integer, String>() {{
+ put(1, "\"ssid1\"");
+ put(2, "\"ssid2\"");
+ put(3, "\"ssid3\"");
+ }};
+ private static final int SUPPLICANT_NETWORK_ID = 2;
+ private static final String SUPPLICANT_SSID = NETWORK_ID_TO_SSID.get(SUPPLICANT_NETWORK_ID);
+ private static final int ROAM_NETWORK_ID = 4;
+ private static final String BSSID = "fa:45:23:23:12:12";
+ private static final String WLAN_IFACE_NAME = "wlan0";
+ private static final String P2P_IFACE_NAME = "p2p0";
+ private static final String ICON_FILE_NAME = "blahblah";
+ private static final int ICON_FILE_SIZE = 72;
+ private static final String HS20_URL = "http://blahblah";
+
+ @Mock IServiceManager mServiceManagerMock;
+ @Mock ISupplicant mISupplicantMock;
+ @Mock ISupplicantIface mISupplicantIfaceMock;
+ @Mock ISupplicantStaIface mISupplicantStaIfaceMock;
+ @Mock Context mContext;
+ @Mock WifiMonitor mWifiMonitor;
+ @Mock SupplicantStaNetworkHal mSupplicantStaNetworkMock;
+ SupplicantStatus mStatusSuccess;
+ SupplicantStatus mStatusFailure;
+ ISupplicant.IfaceInfo mStaIface;
+ ISupplicant.IfaceInfo mP2pIface;
+ ArrayList<ISupplicant.IfaceInfo> mIfaceInfoList;
+ ISupplicantStaIfaceCallback mISupplicantStaIfaceCallback;
+ private SupplicantStaIfaceHal mDut;
+ private ArgumentCaptor<IHwBinder.DeathRecipient> mServiceManagerDeathCaptor =
+ ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
+ private ArgumentCaptor<IHwBinder.DeathRecipient> mSupplicantDeathCaptor =
+ ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
+ private ArgumentCaptor<IHwBinder.DeathRecipient> mSupplicantStaIfaceDeathCaptor =
+ ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
+ private ArgumentCaptor<IServiceNotification.Stub> mServiceNotificationCaptor =
+ ArgumentCaptor.forClass(IServiceNotification.Stub.class);
+ private InOrder mInOrder;
+
+ private class SupplicantStaIfaceHalSpy extends SupplicantStaIfaceHal {
+ SupplicantStaIfaceHalSpy(Context context, WifiMonitor monitor) {
+ super(context, monitor);
+ }
+
+ @Override
+ protected IServiceManager getServiceManagerMockable() throws RemoteException {
+ return mServiceManagerMock;
+ }
+
+ @Override
+ protected ISupplicant getSupplicantMockable() throws RemoteException {
+ return mISupplicantMock;
+ }
+
+ @Override
+ protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) {
+ return mISupplicantStaIfaceMock;
+ }
+
+ @Override
+ protected SupplicantStaNetworkHal getStaNetworkMockable(
+ ISupplicantStaNetwork iSupplicantStaNetwork) {
+ return mSupplicantStaNetworkMock;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mStatusSuccess = createSupplicantStatus(SupplicantStatusCode.SUCCESS);
+ mStatusFailure = createSupplicantStatus(SupplicantStatusCode.FAILURE_UNKNOWN);
+ mStaIface = createIfaceInfo(IfaceType.STA, WLAN_IFACE_NAME);
+ mP2pIface = createIfaceInfo(IfaceType.P2P, P2P_IFACE_NAME);
+
+ mIfaceInfoList = new ArrayList<>();
+ mIfaceInfoList.add(mStaIface);
+ mIfaceInfoList.add(mP2pIface);
+
+ when(mServiceManagerMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong())).thenReturn(true);
+ when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
+ any(IServiceNotification.Stub.class))).thenReturn(true);
+ when(mISupplicantMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong())).thenReturn(true);
+ when(mISupplicantStaIfaceMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong())).thenReturn(true);
+ mDut = new SupplicantStaIfaceHalSpy(mContext, mWifiMonitor);
+ }
+
+ /**
+ * Sunny day scenario for SupplicantStaIfaceHal initialization
+ * Asserts successful initialization
+ */
+ @Test
+ public void testInitialize_success() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false, false);
+ }
+
+ /**
+ * Tests the initialization flow, with a RemoteException occurring when 'getInterface' is called
+ * Ensures initialization fails.
+ */
+ @Test
+ public void testInitialize_remoteExceptionFailure() throws Exception {
+ executeAndValidateInitializationSequence(true, false, false, false);
+ }
+
+ /**
+ * Tests the initialization flow, with listInterfaces returning 0 interfaces.
+ * Ensures failure
+ */
+ @Test
+ public void testInitialize_zeroInterfacesFailure() throws Exception {
+ executeAndValidateInitializationSequence(false, true, false, false);
+ }
+
+ /**
+ * Tests the initialization flow, with a null interface being returned by getInterface.
+ * Ensures initialization fails.
+ */
+ @Test
+ public void testInitialize_nullInterfaceFailure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, true, false);
+ }
+
+ /**
+ * Tests the initialization flow, with a callback registration failure.
+ * Ensures initialization fails.
+ */
+ @Test
+ public void testInitialize_callbackRegistrationFailure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false, true);
+ }
+
+ /**
+ * Tests the loading of networks using {@link SupplicantStaNetworkHal}.
+ * Fills up only the SSID field of configs and uses it as a configKey as well.
+ */
+ @Test
+ public void testLoadNetworks() throws Exception {
+ executeAndValidateInitializationSequence();
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.listNetworksCallback cb) {
+ cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
+ // Reset the |mSupplicantStaNetwork| mock for each network.
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public boolean answer(
+ WifiConfiguration config, Map<String, String> networkExtra) {
+ config.SSID = NETWORK_ID_TO_SSID.get(networkId);
+ config.networkId = networkId;
+ networkExtra.put(
+ SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY, config.SSID);
+ return true;
+ }
+ }).when(mSupplicantStaNetworkMock)
+ .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
+ cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
+ return;
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
+
+ Map<String, WifiConfiguration> configs = new HashMap<>();
+ SparseArray<Map<String, String>> extras = new SparseArray<>();
+ assertTrue(mDut.loadNetworks(configs, extras));
+
+ assertEquals(3, configs.size());
+ assertEquals(3, extras.size());
+ for (Map.Entry<Integer, String> network : NETWORK_ID_TO_SSID.entrySet()) {
+ WifiConfiguration config = configs.get(network.getValue());
+ assertTrue(config != null);
+ assertEquals(network.getKey(), Integer.valueOf(config.networkId));
+ assertEquals(network.getValue(), config.SSID);
+ assertEquals(IpConfiguration.IpAssignment.DHCP, config.getIpAssignment());
+ assertEquals(IpConfiguration.ProxySettings.NONE, config.getProxySettings());
+ }
+ }
+
+ /**
+ * Tests the loading of networks using {@link SupplicantStaNetworkHal} removes any networks
+ * with duplicate config key.
+ * Fills up only the SSID field of configs and uses it as a configKey as well.
+ */
+ @Test
+ public void testLoadNetworksRemovesDuplicates() throws Exception {
+ // Network ID which will have the same config key as the previous one.
+ final int duplicateNetworkId = 2;
+ final int toRemoveNetworkId = duplicateNetworkId - 1;
+ executeAndValidateInitializationSequence();
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.listNetworksCallback cb) {
+ cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public SupplicantStatus answer(int id) {
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaIfaceMock).removeNetwork(eq(toRemoveNetworkId));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
+ // Reset the |mSupplicantStaNetwork| mock for each network.
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public boolean answer(
+ WifiConfiguration config, Map<String, String> networkExtra) {
+ config.SSID = NETWORK_ID_TO_SSID.get(networkId);
+ config.networkId = networkId;
+ // Duplicate network gets the same config key as the to removed one.
+ if (networkId == duplicateNetworkId) {
+ networkExtra.put(
+ SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY,
+ NETWORK_ID_TO_SSID.get(toRemoveNetworkId));
+ } else {
+ networkExtra.put(
+ SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY,
+ NETWORK_ID_TO_SSID.get(networkId));
+ }
+ return true;
+ }
+ }).when(mSupplicantStaNetworkMock)
+ .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
+ cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
+ return;
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
+
+ Map<String, WifiConfiguration> configs = new HashMap<>();
+ SparseArray<Map<String, String>> extras = new SparseArray<>();
+ assertTrue(mDut.loadNetworks(configs, extras));
+
+ assertEquals(2, configs.size());
+ assertEquals(2, extras.size());
+ for (Map.Entry<Integer, String> network : NETWORK_ID_TO_SSID.entrySet()) {
+ if (network.getKey() == toRemoveNetworkId) {
+ continue;
+ }
+ WifiConfiguration config;
+ // Duplicate network gets the same config key as the to removed one. So, use that to
+ // lookup the map.
+ if (network.getKey() == duplicateNetworkId) {
+ config = configs.get(NETWORK_ID_TO_SSID.get(toRemoveNetworkId));
+ } else {
+ config = configs.get(network.getValue());
+ }
+ assertTrue(config != null);
+ assertEquals(network.getKey(), Integer.valueOf(config.networkId));
+ assertEquals(network.getValue(), config.SSID);
+ assertEquals(IpConfiguration.IpAssignment.DHCP, config.getIpAssignment());
+ assertEquals(IpConfiguration.ProxySettings.NONE, config.getProxySettings());
+ }
+ }
+
+ /**
+ * Tests the failure to load networks because of listNetworks failure.
+ */
+ @Test
+ public void testLoadNetworksFailedDueToListNetworks() throws Exception {
+ executeAndValidateInitializationSequence();
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.listNetworksCallback cb) {
+ cb.onValues(mStatusFailure, null);
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
+
+ Map<String, WifiConfiguration> configs = new HashMap<>();
+ SparseArray<Map<String, String>> extras = new SparseArray<>();
+ assertFalse(mDut.loadNetworks(configs, extras));
+ }
+
+ /**
+ * Tests the failure to load networks because of getNetwork failure.
+ */
+ @Test
+ public void testLoadNetworksFailedDueToGetNetwork() throws Exception {
+ executeAndValidateInitializationSequence();
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.listNetworksCallback cb) {
+ cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
+ cb.onValues(mStatusFailure, mock(ISupplicantStaNetwork.class));
+ return;
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
+
+ Map<String, WifiConfiguration> configs = new HashMap<>();
+ SparseArray<Map<String, String>> extras = new SparseArray<>();
+ assertFalse(mDut.loadNetworks(configs, extras));
+ }
+
+ /**
+ * Tests the failure to load networks because of loadWifiConfiguration failure.
+ */
+ @Test
+ public void testLoadNetworksFailedDueToLoadWifiConfiguration() throws Exception {
+ executeAndValidateInitializationSequence();
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.listNetworksCallback cb) {
+ cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
+ cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
+ return;
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public boolean answer(WifiConfiguration config, Map<String, String> networkExtra) {
+ return false;
+ }
+ }).when(mSupplicantStaNetworkMock)
+ .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
+
+ Map<String, WifiConfiguration> configs = new HashMap<>();
+ SparseArray<Map<String, String>> extras = new SparseArray<>();
+ assertTrue(mDut.loadNetworks(configs, extras));
+ assertTrue(configs.isEmpty());
+ }
+
+ /**
+ * Tests the failure to load networks because of loadWifiConfiguration exception.
+ */
+ @Test
+ public void testLoadNetworksFailedDueToExceptionInLoadWifiConfiguration() throws Exception {
+ executeAndValidateInitializationSequence();
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.listNetworksCallback cb) {
+ cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantStaIface.getNetworkCallback cb) {
+ cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
+ return;
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantStaIface.getNetworkCallback.class));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public boolean answer(WifiConfiguration config, Map<String, String> networkExtra)
+ throws Exception {
+ throw new IllegalArgumentException();
+ }
+ }).when(mSupplicantStaNetworkMock)
+ .loadWifiConfiguration(any(WifiConfiguration.class), any(Map.class));
+
+ Map<String, WifiConfiguration> configs = new HashMap<>();
+ SparseArray<Map<String, String>> extras = new SparseArray<>();
+ assertTrue(mDut.loadNetworks(configs, extras));
+ assertTrue(configs.isEmpty());
+ }
+
+ /**
+ * Tests connection to a specified network with empty existing network.
+ */
+ @Test
+ public void testConnectWithEmptyExistingNetwork() throws Exception {
+ executeAndValidateInitializationSequence();
+ executeAndValidateConnectSequence(0, false);
+ }
+
+ @Test
+ public void testConnectToNetworkWithDifferentConfigReplacesNetworkInSupplicant()
+ throws Exception {
+ executeAndValidateInitializationSequence();
+ WifiConfiguration config = executeAndValidateConnectSequence(
+ SUPPLICANT_NETWORK_ID, false);
+ // Reset mocks for mISupplicantStaIfaceMock because we finished the first connection.
+ reset(mISupplicantStaIfaceMock);
+ setupMocksForConnectSequence(true /*haveExistingNetwork*/);
+ // Make this network different by changing SSID.
+ config.SSID = "AnDifferentSSID";
+ assertTrue(mDut.connectToNetwork(config));
+ verify(mISupplicantStaIfaceMock).removeNetwork(SUPPLICANT_NETWORK_ID);
+ verify(mISupplicantStaIfaceMock)
+ .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
+ }
+
+ @Test
+ public void connectToNetworkWithSameNetworkDoesNotRemoveNetworkFromSupplicant()
+ throws Exception {
+ executeAndValidateInitializationSequence();
+ WifiConfiguration config = executeAndValidateConnectSequence(SUPPLICANT_NETWORK_ID, false);
+ // Reset mocks for mISupplicantStaIfaceMock because we finished the first connection.
+ reset(mISupplicantStaIfaceMock);
+ setupMocksForConnectSequence(true /*haveExistingNetwork*/);
+ assertTrue(mDut.connectToNetwork(config));
+ verify(mISupplicantStaIfaceMock, never()).removeNetwork(anyInt());
+ verify(mISupplicantStaIfaceMock, never())
+ .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
+ }
+
+ /**
+ * Tests connection to a specified network failure due to network add.
+ */
+ @Test
+ public void testConnectFailureDueToNetworkAddFailure() throws Exception {
+ executeAndValidateInitializationSequence();
+ setupMocksForConnectSequence(false);
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.addNetworkCallback cb) throws RemoteException {
+ cb.onValues(mStatusFailure, mock(ISupplicantStaNetwork.class));
+ return;
+ }
+ }).when(mISupplicantStaIfaceMock).addNetwork(
+ any(ISupplicantStaIface.addNetworkCallback.class));
+
+ assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
+ }
+
+ /**
+ * Tests connection to a specified network failure due to network save.
+ */
+ @Test
+ public void testConnectFailureDueToNetworkSaveFailure() throws Exception {
+ executeAndValidateInitializationSequence();
+ setupMocksForConnectSequence(true);
+
+ when(mSupplicantStaNetworkMock.saveWifiConfiguration(any(WifiConfiguration.class)))
+ .thenReturn(false);
+
+ assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
+ // We should have removed the existing network once before connection and once more
+ // on failure to save network configuration.
+ verify(mISupplicantStaIfaceMock, times(2)).removeNetwork(anyInt());
+ }
+
+ /**
+ * Tests connection to a specified network failure due to exception in network save.
+ */
+ @Test
+ public void testConnectFailureDueToNetworkSaveException() throws Exception {
+ executeAndValidateInitializationSequence();
+ setupMocksForConnectSequence(true);
+
+ doThrow(new IllegalArgumentException("Some error!!!"))
+ .when(mSupplicantStaNetworkMock).saveWifiConfiguration(
+ any(WifiConfiguration.class));
+
+ assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
+ // We should have removed the existing network once before connection and once more
+ // on failure to save network configuration.
+ verify(mISupplicantStaIfaceMock, times(2)).removeNetwork(anyInt());
+ }
+
+ /**
+ * Tests connection to a specified network failure due to network select.
+ */
+ @Test
+ public void testConnectFailureDueToNetworkSelectFailure() throws Exception {
+ executeAndValidateInitializationSequence();
+ setupMocksForConnectSequence(false);
+
+ when(mSupplicantStaNetworkMock.select()).thenReturn(false);
+
+ assertFalse(mDut.connectToNetwork(createTestWifiConfiguration()));
+ }
+
+ /**
+ * Tests roaming to the same network as the currently connected one.
+ */
+ @Test
+ public void testRoamToSameNetwork() throws Exception {
+ executeAndValidateInitializationSequence();
+ executeAndValidateRoamSequence(true);
+ assertTrue(mDut.connectToNetwork(createTestWifiConfiguration()));
+ }
+
+ /**
+ * Tests roaming to a different network.
+ */
+ @Test
+ public void testRoamToDifferentNetwork() throws Exception {
+ executeAndValidateInitializationSequence();
+ executeAndValidateRoamSequence(false);
+ }
+
+ /**
+ * Tests roaming failure because of unable to set bssid.
+ */
+ @Test
+ public void testRoamFailureDueToBssidSet() throws Exception {
+ executeAndValidateInitializationSequence();
+ int connectedNetworkId = 5;
+ executeAndValidateConnectSequence(connectedNetworkId, false);
+ when(mSupplicantStaNetworkMock.setBssid(anyString())).thenReturn(false);
+
+ WifiConfiguration roamingConfig = new WifiConfiguration();
+ roamingConfig.networkId = connectedNetworkId;
+ roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID("45:34:23:23:ab:ed");
+ assertFalse(mDut.roamToNetwork(roamingConfig));
+ }
+
+ /**
+ * Tests removal of all configured networks from wpa_supplicant.
+ */
+ @Test
+ public void testRemoveAllNetworks() throws Exception {
+ executeAndValidateInitializationSequence();
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.listNetworksCallback cb) {
+ cb.onValues(mStatusSuccess, new ArrayList<>(NETWORK_ID_TO_SSID.keySet()));
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public SupplicantStatus answer(int id) {
+ assertTrue(NETWORK_ID_TO_SSID.containsKey(id));
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaIfaceMock).removeNetwork(anyInt());
+
+ assertTrue(mDut.removeAllNetworks());
+ verify(mISupplicantStaIfaceMock, times(NETWORK_ID_TO_SSID.size())).removeNetwork(anyInt());
+ }
+
+ /**
+ * Remove all networks while connected, verify that the current network info is resetted.
+ */
+ @Test
+ public void testRemoveAllNetworksWhileConnected() throws Exception {
+ String testBssid = "11:22:33:44:55:66";
+ when(mSupplicantStaNetworkMock.setBssid(eq(testBssid))).thenReturn(true);
+
+ executeAndValidateInitializationSequence();
+
+ // Connect to a network and verify current network is set.
+ executeAndValidateConnectSequence(4, false);
+ assertTrue(mDut.setCurrentNetworkBssid(testBssid));
+ verify(mSupplicantStaNetworkMock).setBssid(eq(testBssid));
+ reset(mSupplicantStaNetworkMock);
+
+ // Remove all networks and verify current network info is resetted.
+ assertTrue(mDut.removeAllNetworks());
+ assertFalse(mDut.setCurrentNetworkBssid(testBssid));
+ verify(mSupplicantStaNetworkMock, never()).setBssid(eq(testBssid));
+ }
+
+ /**
+ * Tests roaming failure because of unable to reassociate.
+ */
+ @Test
+ public void testRoamFailureDueToReassociate() throws Exception {
+ executeAndValidateInitializationSequence();
+ int connectedNetworkId = 5;
+ executeAndValidateConnectSequence(connectedNetworkId, false);
+
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public SupplicantStatus answer() throws RemoteException {
+ return mStatusFailure;
+ }
+ }).when(mISupplicantStaIfaceMock).reassociate();
+ when(mSupplicantStaNetworkMock.setBssid(anyString())).thenReturn(true);
+
+ WifiConfiguration roamingConfig = new WifiConfiguration();
+ roamingConfig.networkId = connectedNetworkId;
+ roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID("45:34:23:23:ab:ed");
+ assertFalse(mDut.roamToNetwork(roamingConfig));
+ }
+
+ /**
+ * Tests the retrieval of WPS NFC token.
+ */
+ @Test
+ public void testGetCurrentNetworkWpsNfcConfigurationToken() throws Exception {
+ String token = "45adbc1";
+ when(mSupplicantStaNetworkMock.getWpsNfcConfigurationToken()).thenReturn(token);
+
+ executeAndValidateInitializationSequence();
+ // Return null when not connected to the network.
+ assertTrue(mDut.getCurrentNetworkWpsNfcConfigurationToken() == null);
+ verify(mSupplicantStaNetworkMock, never()).getWpsNfcConfigurationToken();
+ executeAndValidateConnectSequence(4, false);
+ assertEquals(token, mDut.getCurrentNetworkWpsNfcConfigurationToken());
+ verify(mSupplicantStaNetworkMock).getWpsNfcConfigurationToken();
+ }
+
+ /**
+ * Tests the setting of BSSID.
+ */
+ @Test
+ public void testSetCurrentNetworkBssid() throws Exception {
+ String bssidStr = "34:34:12:12:12:90";
+ when(mSupplicantStaNetworkMock.setBssid(eq(bssidStr))).thenReturn(true);
+
+ executeAndValidateInitializationSequence();
+ // Fail when not connected to a network.
+ assertFalse(mDut.setCurrentNetworkBssid(bssidStr));
+ verify(mSupplicantStaNetworkMock, never()).setBssid(eq(bssidStr));
+ executeAndValidateConnectSequence(4, false);
+ assertTrue(mDut.setCurrentNetworkBssid(bssidStr));
+ verify(mSupplicantStaNetworkMock).setBssid(eq(bssidStr));
+ }
+
+ /**
+ * Tests the sending identity response for the current network.
+ */
+ @Test
+ public void testSetCurrentNetworkEapIdentityResponse() throws Exception {
+ String identity = "blah@blah.com";
+ when(mSupplicantStaNetworkMock.sendNetworkEapIdentityResponse(eq(identity)))
+ .thenReturn(true);
+
+ executeAndValidateInitializationSequence();
+ // Fail when not connected to a network.
+ assertFalse(mDut.sendCurrentNetworkEapIdentityResponse(identity));
+ verify(mSupplicantStaNetworkMock, never()).sendNetworkEapIdentityResponse(eq(identity));
+ executeAndValidateConnectSequence(4, false);
+ assertTrue(mDut.sendCurrentNetworkEapIdentityResponse(identity));
+ verify(mSupplicantStaNetworkMock).sendNetworkEapIdentityResponse(eq(identity));
+ }
+
+ /**
+ * Tests the getting of anonymous identity for the current network.
+ */
+ @Test
+ public void testGetCurrentNetworkEapAnonymousIdentity() throws Exception {
+ String anonymousIdentity = "aaa@bbb.ccc";
+ when(mSupplicantStaNetworkMock.fetchEapAnonymousIdentity())
+ .thenReturn(anonymousIdentity);
+ executeAndValidateInitializationSequence();
+
+ // Return null when not connected to the network.
+ assertEquals(null, mDut.getCurrentNetworkEapAnonymousIdentity());
+ executeAndValidateConnectSequence(4, false);
+ // Return anonymous identity for the current network.
+ assertEquals(anonymousIdentity, mDut.getCurrentNetworkEapAnonymousIdentity());
+ }
+
+ /**
+ * Tests the sending gsm auth response for the current network.
+ */
+ @Test
+ public void testSetCurrentNetworkEapSimGsmAuthResponse() throws Exception {
+ String params = "test";
+ when(mSupplicantStaNetworkMock.sendNetworkEapSimGsmAuthResponse(eq(params)))
+ .thenReturn(true);
+
+ executeAndValidateInitializationSequence();
+ // Fail when not connected to a network.
+ assertFalse(mDut.sendCurrentNetworkEapSimGsmAuthResponse(params));
+ verify(mSupplicantStaNetworkMock, never()).sendNetworkEapSimGsmAuthResponse(eq(params));
+ executeAndValidateConnectSequence(4, false);
+ assertTrue(mDut.sendCurrentNetworkEapSimGsmAuthResponse(params));
+ verify(mSupplicantStaNetworkMock).sendNetworkEapSimGsmAuthResponse(eq(params));
+ }
+
+ /**
+ * Tests the sending umts auth response for the current network.
+ */
+ @Test
+ public void testSetCurrentNetworkEapSimUmtsAuthResponse() throws Exception {
+ String params = "test";
+ when(mSupplicantStaNetworkMock.sendNetworkEapSimUmtsAuthResponse(eq(params)))
+ .thenReturn(true);
+
+ executeAndValidateInitializationSequence();
+ // Fail when not connected to a network.
+ assertFalse(mDut.sendCurrentNetworkEapSimUmtsAuthResponse(params));
+ verify(mSupplicantStaNetworkMock, never()).sendNetworkEapSimUmtsAuthResponse(eq(params));
+ executeAndValidateConnectSequence(4, false);
+ assertTrue(mDut.sendCurrentNetworkEapSimUmtsAuthResponse(params));
+ verify(mSupplicantStaNetworkMock).sendNetworkEapSimUmtsAuthResponse(eq(params));
+ }
+
+ /**
+ * Tests the sending umts auts response for the current network.
+ */
+ @Test
+ public void testSetCurrentNetworkEapSimUmtsAutsResponse() throws Exception {
+ String params = "test";
+ when(mSupplicantStaNetworkMock.sendNetworkEapSimUmtsAutsResponse(eq(params)))
+ .thenReturn(true);
+
+ executeAndValidateInitializationSequence();
+ // Fail when not connected to a network.
+ assertFalse(mDut.sendCurrentNetworkEapSimUmtsAutsResponse(params));
+ verify(mSupplicantStaNetworkMock, never()).sendNetworkEapSimUmtsAutsResponse(eq(params));
+ executeAndValidateConnectSequence(4, false);
+ assertTrue(mDut.sendCurrentNetworkEapSimUmtsAutsResponse(params));
+ verify(mSupplicantStaNetworkMock).sendNetworkEapSimUmtsAutsResponse(eq(params));
+ }
+
+ /**
+ * Tests the setting of WPS device type.
+ */
+ @Test
+ public void testSetWpsDeviceType() throws Exception {
+ String validDeviceTypeStr = "10-0050F204-5";
+ byte[] expectedDeviceType = { 0x0, 0xa, 0x0, 0x50, (byte) 0xf2, 0x04, 0x0, 0x05};
+ String invalidDeviceType1Str = "10-02050F204-5";
+ String invalidDeviceType2Str = "10-0050F204-534";
+ when(mISupplicantStaIfaceMock.setWpsDeviceType(any(byte[].class)))
+ .thenReturn(mStatusSuccess);
+
+ executeAndValidateInitializationSequence();
+
+ // This should work.
+ assertTrue(mDut.setWpsDeviceType(validDeviceTypeStr));
+ verify(mISupplicantStaIfaceMock).setWpsDeviceType(eq(expectedDeviceType));
+
+ // This should not work
+ assertFalse(mDut.setWpsDeviceType(invalidDeviceType1Str));
+ // This should not work
+ assertFalse(mDut.setWpsDeviceType(invalidDeviceType2Str));
+ }
+
+ /**
+ * Tests the setting of WPS config methods.
+ */
+ @Test
+ public void testSetWpsConfigMethods() throws Exception {
+ String validConfigMethodsStr = "physical_display virtual_push_button";
+ Short expectedConfigMethods =
+ WpsConfigMethods.PHY_DISPLAY | WpsConfigMethods.VIRT_PUSHBUTTON;
+ String invalidConfigMethodsStr = "physical_display virtual_push_button test";
+ when(mISupplicantStaIfaceMock.setWpsConfigMethods(anyShort())).thenReturn(mStatusSuccess);
+
+ executeAndValidateInitializationSequence();
+
+ // This should work.
+ assertTrue(mDut.setWpsConfigMethods(validConfigMethodsStr));
+ verify(mISupplicantStaIfaceMock).setWpsConfigMethods(eq(expectedConfigMethods));
+
+ // This should throw an illegal argument exception.
+ try {
+ assertFalse(mDut.setWpsConfigMethods(invalidConfigMethodsStr));
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+ assertTrue(false);
+ }
+
+ /**
+ * Tests the handling of ANQP done callback.
+ * Note: Since the ANQP element parsing methods are static, this can only test the negative test
+ * where all the parsing fails because the data is empty. It'll be non-trivial and unnecessary
+ * to test out the parsing logic here.
+ */
+ @Test
+ public void testAnqpDoneCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+ byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
+ mISupplicantStaIfaceCallback.onAnqpQueryDone(
+ bssid, new ISupplicantStaIfaceCallback.AnqpData(),
+ new ISupplicantStaIfaceCallback.Hs20AnqpData());
+
+ ArgumentCaptor<AnqpEvent> anqpEventCaptor = ArgumentCaptor.forClass(AnqpEvent.class);
+ verify(mWifiMonitor).broadcastAnqpDoneEvent(eq(WLAN_IFACE_NAME), anqpEventCaptor.capture());
+ assertEquals(
+ ByteBufferReader.readInteger(
+ ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
+ anqpEventCaptor.getValue().getBssid());
+ }
+
+ /**
+ * Tests the handling of Icon done callback.
+ */
+ @Test
+ public void testIconDoneCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
+ byte[] iconData = new byte[ICON_FILE_SIZE];
+ new Random().nextBytes(iconData);
+ mISupplicantStaIfaceCallback.onHs20IconQueryDone(
+ bssid, ICON_FILE_NAME, NativeUtil.byteArrayToArrayList(iconData));
+
+ ArgumentCaptor<IconEvent> iconEventCaptor = ArgumentCaptor.forClass(IconEvent.class);
+ verify(mWifiMonitor).broadcastIconDoneEvent(eq(WLAN_IFACE_NAME), iconEventCaptor.capture());
+ assertEquals(
+ ByteBufferReader.readInteger(
+ ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
+ iconEventCaptor.getValue().getBSSID());
+ assertEquals(ICON_FILE_NAME, iconEventCaptor.getValue().getFileName());
+ assertArrayEquals(iconData, iconEventCaptor.getValue().getData());
+ }
+
+ /**
+ * Tests the handling of HS20 subscription remediation callback.
+ */
+ @Test
+ public void testHs20SubscriptionRemediationCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
+ byte osuMethod = ISupplicantStaIfaceCallback.OsuMethod.OMA_DM;
+ mISupplicantStaIfaceCallback.onHs20SubscriptionRemediation(
+ bssid, osuMethod, HS20_URL);
+
+ ArgumentCaptor<WnmData> wnmDataCaptor = ArgumentCaptor.forClass(WnmData.class);
+ verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN_IFACE_NAME), wnmDataCaptor.capture());
+ assertEquals(
+ ByteBufferReader.readInteger(
+ ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
+ wnmDataCaptor.getValue().getBssid());
+ assertEquals(osuMethod, wnmDataCaptor.getValue().getMethod());
+ assertEquals(HS20_URL, wnmDataCaptor.getValue().getUrl());
+ }
+
+ /**
+ * Tests the handling of HS20 deauth imminent callback.
+ */
+ @Test
+ public void testHs20DeauthImminentCallbackWithEssReasonCode() throws Exception {
+ executeAndValidateHs20DeauthImminentCallback(true);
+ }
+
+ /**
+ * Tests the handling of HS20 deauth imminent callback.
+ */
+ @Test
+ public void testHs20DeauthImminentCallbackWithNonEssReasonCode() throws Exception {
+ executeAndValidateHs20DeauthImminentCallback(false);
+
+ }
+
+ /**
+ * Tests the handling of state change notification without any configured network.
+ */
+ @Test
+ public void testStateChangeCallbackWithNoConfiguredNetwork() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ mISupplicantStaIfaceCallback.onStateChanged(
+ ISupplicantStaIfaceCallback.State.INACTIVE,
+ NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID,
+ NativeUtil.decodeSsid(SUPPLICANT_SSID));
+
+ // Can't compare WifiSsid instances because they lack an equals.
+ verify(mWifiMonitor).broadcastSupplicantStateChangeEvent(
+ eq(WLAN_IFACE_NAME), eq(WifiConfiguration.INVALID_NETWORK_ID),
+ any(WifiSsid.class), eq(BSSID), eq(SupplicantState.INACTIVE));
+ }
+
+ /**
+ * Tests the handling of state change notification to associated after configuring a network.
+ */
+ @Test
+ public void testStateChangeToAssociatedCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ int frameworkNetworkId = 6;
+ executeAndValidateConnectSequence(frameworkNetworkId, false);
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ mISupplicantStaIfaceCallback.onStateChanged(
+ ISupplicantStaIfaceCallback.State.ASSOCIATED,
+ NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID,
+ NativeUtil.decodeSsid(SUPPLICANT_SSID));
+
+ verify(mWifiMonitor).broadcastSupplicantStateChangeEvent(
+ eq(WLAN_IFACE_NAME), eq(frameworkNetworkId),
+ any(WifiSsid.class), eq(BSSID), eq(SupplicantState.ASSOCIATED));
+ }
+
+ /**
+ * Tests the handling of state change notification to completed after configuring a network.
+ */
+ @Test
+ public void testStateChangeToCompletedCallback() throws Exception {
+ InOrder wifiMonitorInOrder = inOrder(mWifiMonitor);
+ executeAndValidateInitializationSequence();
+ int frameworkNetworkId = 6;
+ executeAndValidateConnectSequence(frameworkNetworkId, false);
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ mISupplicantStaIfaceCallback.onStateChanged(
+ ISupplicantStaIfaceCallback.State.COMPLETED,
+ NativeUtil.macAddressToByteArray(BSSID), SUPPLICANT_NETWORK_ID,
+ NativeUtil.decodeSsid(SUPPLICANT_SSID));
+
+ wifiMonitorInOrder.verify(mWifiMonitor).broadcastNetworkConnectionEvent(
+ eq(WLAN_IFACE_NAME), eq(frameworkNetworkId), eq(BSSID));
+ wifiMonitorInOrder.verify(mWifiMonitor).broadcastSupplicantStateChangeEvent(
+ eq(WLAN_IFACE_NAME), eq(frameworkNetworkId),
+ any(WifiSsid.class), eq(BSSID), eq(SupplicantState.COMPLETED));
+ }
+
+ /**
+ * Tests the handling of network disconnected notification.
+ */
+ @Test
+ public void testDisconnectedCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ int reasonCode = 5;
+ mISupplicantStaIfaceCallback.onDisconnected(
+ NativeUtil.macAddressToByteArray(BSSID), true, reasonCode);
+ verify(mWifiMonitor).broadcastNetworkDisconnectionEvent(
+ eq(WLAN_IFACE_NAME), eq(1), eq(reasonCode), eq(BSSID));
+
+ mISupplicantStaIfaceCallback.onDisconnected(
+ NativeUtil.macAddressToByteArray(BSSID), false, reasonCode);
+ verify(mWifiMonitor).broadcastNetworkDisconnectionEvent(
+ eq(WLAN_IFACE_NAME), eq(0), eq(reasonCode), eq(BSSID));
+ }
+
+ /**
+ * Tests the handling of incorrect network passwords.
+ */
+ @Test
+ public void testAuthFailurePassword() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ int reasonCode = 3;
+ mISupplicantStaIfaceCallback.onDisconnected(
+ NativeUtil.macAddressToByteArray(BSSID), true, reasonCode);
+ verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt());
+
+ mISupplicantStaIfaceCallback.onDisconnected(
+ NativeUtil.macAddressToByteArray(BSSID), false, reasonCode);
+ verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt());
+
+ mISupplicantStaIfaceCallback.onStateChanged(
+ ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE,
+ NativeUtil.macAddressToByteArray(BSSID),
+ SUPPLICANT_NETWORK_ID,
+ NativeUtil.decodeSsid(SUPPLICANT_SSID));
+ mISupplicantStaIfaceCallback.onDisconnected(
+ NativeUtil.macAddressToByteArray(BSSID), true, reasonCode);
+ mISupplicantStaIfaceCallback.onDisconnected(
+ NativeUtil.macAddressToByteArray(BSSID), false, reasonCode);
+
+ verify(mWifiMonitor, times(2)).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
+ eq(WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD));
+
+ }
+
+ /**
+ * Tests the handling of incorrect network passwords, edge case.
+ *
+ * If the disconnect reason is "IE in 4way differs", do not call it a password mismatch.
+ */
+ @Test
+ public void testIeDiffers() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ int reasonCode = 17; // IEEE 802.11i WLAN_REASON_IE_IN_4WAY_DIFFERS
+
+ mISupplicantStaIfaceCallback.onStateChanged(
+ ISupplicantStaIfaceCallback.State.FOURWAY_HANDSHAKE,
+ NativeUtil.macAddressToByteArray(BSSID),
+ SUPPLICANT_NETWORK_ID,
+ NativeUtil.decodeSsid(SUPPLICANT_SSID));
+ mISupplicantStaIfaceCallback.onDisconnected(
+ NativeUtil.macAddressToByteArray(BSSID), true, reasonCode);
+ verify(mWifiMonitor, times(0)).broadcastAuthenticationFailureEvent(any(), anyInt());
+ }
+
+
+ /**
+ * Tests the handling of association rejection notification.
+ */
+ @Test
+ public void testAssociationRejectionCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ int statusCode = 7;
+ mISupplicantStaIfaceCallback.onAssociationRejected(
+ NativeUtil.macAddressToByteArray(BSSID), statusCode, false);
+ verify(mWifiMonitor).broadcastAssociationRejectionEvent(
+ eq(WLAN_IFACE_NAME), eq(statusCode), eq(false), eq(BSSID));
+ }
+
+ /**
+ * Tests the handling of authentification timeout notification.
+ */
+ @Test
+ public void testAuthenticationTimeoutCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ mISupplicantStaIfaceCallback.onAuthenticationTimeout(
+ NativeUtil.macAddressToByteArray(BSSID));
+ verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
+ eq(WifiManager.ERROR_AUTH_FAILURE_TIMEOUT));
+ }
+
+ /**
+ * Tests the handling of bssid change notification.
+ */
+ @Test
+ public void testBssidChangedCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ mISupplicantStaIfaceCallback.onBssidChanged(
+ BssidChangeReason.ASSOC_START, NativeUtil.macAddressToByteArray(BSSID));
+ verify(mWifiMonitor).broadcastTargetBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
+ verify(mWifiMonitor, never()).broadcastAssociatedBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
+
+ reset(mWifiMonitor);
+ mISupplicantStaIfaceCallback.onBssidChanged(
+ BssidChangeReason.ASSOC_COMPLETE, NativeUtil.macAddressToByteArray(BSSID));
+ verify(mWifiMonitor, never()).broadcastTargetBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
+ verify(mWifiMonitor).broadcastAssociatedBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
+
+ reset(mWifiMonitor);
+ mISupplicantStaIfaceCallback.onBssidChanged(
+ BssidChangeReason.DISASSOC, NativeUtil.macAddressToByteArray(BSSID));
+ verify(mWifiMonitor, never()).broadcastTargetBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
+ verify(mWifiMonitor, never()).broadcastAssociatedBssidEvent(eq(WLAN_IFACE_NAME), eq(BSSID));
+ }
+
+ /**
+ * Tests the handling of EAP failure notification.
+ */
+ @Test
+ public void testEapFailureCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ mISupplicantStaIfaceCallback.onEapFailure();
+ verify(mWifiMonitor).broadcastAuthenticationFailureEvent(eq(WLAN_IFACE_NAME),
+ eq(WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE));
+ }
+
+ /**
+ * Tests the handling of Wps success notification.
+ */
+ @Test
+ public void testWpsSuccessCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ mISupplicantStaIfaceCallback.onWpsEventSuccess();
+ verify(mWifiMonitor).broadcastWpsSuccessEvent(eq(WLAN_IFACE_NAME));
+ }
+
+ /**
+ * Tests the handling of Wps fail notification.
+ */
+ @Test
+ public void testWpsFailureCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ short cfgError = ISupplicantStaIfaceCallback.WpsConfigError.MULTIPLE_PBC_DETECTED;
+ short errorInd = ISupplicantStaIfaceCallback.WpsErrorIndication.SECURITY_WEP_PROHIBITED;
+ mISupplicantStaIfaceCallback.onWpsEventFail(
+ NativeUtil.macAddressToByteArray(BSSID), cfgError, errorInd);
+ verify(mWifiMonitor).broadcastWpsFailEvent(eq(WLAN_IFACE_NAME),
+ eq((int) cfgError), eq((int) errorInd));
+ }
+
+ /**
+ * Tests the handling of Wps fail notification.
+ */
+ @Test
+ public void testWpsTimeoutCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ short cfgError = ISupplicantStaIfaceCallback.WpsConfigError.MSG_TIMEOUT;
+ short errorInd = ISupplicantStaIfaceCallback.WpsErrorIndication.NO_ERROR;
+ mISupplicantStaIfaceCallback.onWpsEventFail(
+ NativeUtil.macAddressToByteArray(BSSID), cfgError, errorInd);
+ verify(mWifiMonitor).broadcastWpsTimeoutEvent(eq(WLAN_IFACE_NAME));
+ }
+
+ /**
+ * Tests the handling of Wps pbc overlap notification.
+ */
+ @Test
+ public void testWpsPbcOverlapCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ mISupplicantStaIfaceCallback.onWpsEventPbcOverlap();
+ verify(mWifiMonitor).broadcastWpsOverlapEvent(eq(WLAN_IFACE_NAME));
+ }
+
+ /**
+ * Tests the handling of service manager death notification.
+ */
+ @Test
+ public void testServiceManagerDeathCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mServiceManagerDeathCaptor.getValue());
+ assertTrue(mDut.isInitializationComplete());
+
+ mServiceManagerDeathCaptor.getValue().serviceDied(5L);
+
+ assertFalse(mDut.isInitializationComplete());
+ verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN_IFACE_NAME));
+ }
+
+ /**
+ * Tests the handling of supplicant death notification.
+ */
+ @Test
+ public void testSupplicantDeathCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mSupplicantDeathCaptor.getValue());
+ assertTrue(mDut.isInitializationComplete());
+
+ mSupplicantDeathCaptor.getValue().serviceDied(5L);
+
+ assertFalse(mDut.isInitializationComplete());
+ verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN_IFACE_NAME));
+ }
+
+ /**
+ * Tests the handling of supplicant sta iface death notification.
+ */
+ @Test
+ public void testSupplicantStaIfaceDeathCallback() throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mSupplicantStaIfaceDeathCaptor.getValue());
+ assertTrue(mDut.isInitializationComplete());
+
+ mSupplicantStaIfaceDeathCaptor.getValue().serviceDied(5L);
+
+ assertFalse(mDut.isInitializationComplete());
+ verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(WLAN_IFACE_NAME));
+ }
+
+ /**
+ * Tests the setting of log level.
+ */
+ @Test
+ public void testSetLogLevel() throws Exception {
+ when(mISupplicantMock.setDebugParams(anyInt(), anyBoolean(), anyBoolean()))
+ .thenReturn(mStatusSuccess);
+
+ // Fail before initialization is performed.
+ assertFalse(mDut.setLogLevel(true));
+
+ executeAndValidateInitializationSequence();
+
+ // This should work.
+ assertTrue(mDut.setLogLevel(true));
+ verify(mISupplicantMock)
+ .setDebugParams(eq(ISupplicant.DebugLevel.DEBUG), eq(false), eq(false));
+ }
+
+ /**
+ * Tests the setting of concurrency priority.
+ */
+ @Test
+ public void testConcurrencyPriority() throws Exception {
+ when(mISupplicantMock.setConcurrencyPriority(anyInt())).thenReturn(mStatusSuccess);
+
+ // Fail before initialization is performed.
+ assertFalse(mDut.setConcurrencyPriority(false));
+
+ executeAndValidateInitializationSequence();
+
+ // This should work.
+ assertTrue(mDut.setConcurrencyPriority(false));
+ verify(mISupplicantMock).setConcurrencyPriority(eq(IfaceType.P2P));
+ assertTrue(mDut.setConcurrencyPriority(true));
+ verify(mISupplicantMock).setConcurrencyPriority(eq(IfaceType.STA));
+ }
+
+ /**
+ * Tests the start of wps registrar.
+ */
+ @Test
+ public void testStartWpsRegistrar() throws Exception {
+ when(mISupplicantStaIfaceMock.startWpsRegistrar(any(byte[].class), anyString()))
+ .thenReturn(mStatusSuccess);
+
+ // Fail before initialization is performed.
+ assertFalse(mDut.startWpsRegistrar(null, null));
+
+ executeAndValidateInitializationSequence();
+
+ assertFalse(mDut.startWpsRegistrar(null, null));
+ verify(mISupplicantStaIfaceMock, never()).startWpsRegistrar(any(byte[].class), anyString());
+
+ assertFalse(mDut.startWpsRegistrar(new String(), "452233"));
+ verify(mISupplicantStaIfaceMock, never()).startWpsRegistrar(any(byte[].class), anyString());
+
+ assertTrue(mDut.startWpsRegistrar("45:23:12:12:12:98", "562535"));
+ verify(mISupplicantStaIfaceMock).startWpsRegistrar(any(byte[].class), anyString());
+ }
+
+ /**
+ * Tests the start of wps PBC.
+ */
+ @Test
+ public void testStartWpsPbc() throws Exception {
+ when(mISupplicantStaIfaceMock.startWpsPbc(any(byte[].class))).thenReturn(mStatusSuccess);
+ String bssid = "45:23:12:12:12:98";
+ byte[] bssidBytes = {0x45, 0x23, 0x12, 0x12, 0x12, (byte) 0x98};
+ byte[] anyBssidBytes = {0, 0, 0, 0, 0, 0};
+
+ // Fail before initialization is performed.
+ assertFalse(mDut.startWpsPbc(bssid));
+ verify(mISupplicantStaIfaceMock, never()).startWpsPbc(any(byte[].class));
+
+ executeAndValidateInitializationSequence();
+
+ assertTrue(mDut.startWpsPbc(bssid));
+ verify(mISupplicantStaIfaceMock).startWpsPbc(eq(bssidBytes));
+
+ assertTrue(mDut.startWpsPbc(null));
+ verify(mISupplicantStaIfaceMock).startWpsPbc(eq(anyBssidBytes));
+ }
+
+ private WifiConfiguration createTestWifiConfiguration() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = SUPPLICANT_NETWORK_ID;
+ return config;
+ }
+
+ private void executeAndValidateHs20DeauthImminentCallback(boolean isEss) throws Exception {
+ executeAndValidateInitializationSequence();
+ assertNotNull(mISupplicantStaIfaceCallback);
+
+ byte[] bssid = NativeUtil.macAddressToByteArray(BSSID);
+ int reasonCode = isEss ? WnmData.ESS : WnmData.ESS + 1;
+ int reauthDelay = 5;
+ mISupplicantStaIfaceCallback.onHs20DeauthImminentNotice(
+ bssid, reasonCode, reauthDelay, HS20_URL);
+
+ ArgumentCaptor<WnmData> wnmDataCaptor = ArgumentCaptor.forClass(WnmData.class);
+ verify(mWifiMonitor).broadcastWnmEvent(eq(WLAN_IFACE_NAME), wnmDataCaptor.capture());
+ assertEquals(
+ ByteBufferReader.readInteger(
+ ByteBuffer.wrap(bssid), ByteOrder.BIG_ENDIAN, bssid.length),
+ wnmDataCaptor.getValue().getBssid());
+ assertEquals(isEss, wnmDataCaptor.getValue().isEss());
+ assertEquals(reauthDelay, wnmDataCaptor.getValue().getDelay());
+ assertEquals(HS20_URL, wnmDataCaptor.getValue().getUrl());
+ }
+
+ private void executeAndValidateInitializationSequence() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false, false);
+ }
+
+ /**
+ * Calls.initialize(), mocking various call back answers and verifying flow, asserting for the
+ * expected result. Verifies if ISupplicantStaIface manager is initialized or reset.
+ * Each of the arguments will cause a different failure mode when set true.
+ */
+ private void executeAndValidateInitializationSequence(boolean causeRemoteException,
+ boolean getZeroInterfaces,
+ boolean getNullInterface,
+ boolean causeCallbackRegFailure)
+ throws Exception {
+ boolean shouldSucceed =
+ !causeRemoteException && !getZeroInterfaces && !getNullInterface
+ && !causeCallbackRegFailure;
+ // Setup callback mock answers
+ ArrayList<ISupplicant.IfaceInfo> interfaces;
+ if (getZeroInterfaces) {
+ interfaces = new ArrayList<>();
+ } else {
+ interfaces = mIfaceInfoList;
+ }
+ doAnswer(new GetListInterfacesAnswer(interfaces)).when(mISupplicantMock)
+ .listInterfaces(any(ISupplicant.listInterfacesCallback.class));
+ if (causeRemoteException) {
+ doThrow(new RemoteException("Some error!!!"))
+ .when(mISupplicantMock).getInterface(any(ISupplicant.IfaceInfo.class),
+ any(ISupplicant.getInterfaceCallback.class));
+ } else {
+ doAnswer(new GetGetInterfaceAnswer(getNullInterface))
+ .when(mISupplicantMock).getInterface(any(ISupplicant.IfaceInfo.class),
+ any(ISupplicant.getInterfaceCallback.class));
+ }
+ /** Callback registeration */
+ if (causeCallbackRegFailure) {
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public SupplicantStatus answer(ISupplicantStaIfaceCallback cb)
+ throws RemoteException {
+ return mStatusFailure;
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .registerCallback(any(ISupplicantStaIfaceCallback.class));
+ } else {
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public SupplicantStatus answer(ISupplicantStaIfaceCallback cb)
+ throws RemoteException {
+ mISupplicantStaIfaceCallback = cb;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .registerCallback(any(ISupplicantStaIfaceCallback.class));
+ }
+
+ mInOrder = inOrder(mServiceManagerMock, mISupplicantMock, mISupplicantStaIfaceMock,
+ mWifiMonitor);
+ // Initialize SupplicantStaIfaceHal, should call serviceManager.registerForNotifications
+ assertTrue(mDut.initialize());
+ // verify: service manager initialization sequence
+ mInOrder.verify(mServiceManagerMock).linkToDeath(mServiceManagerDeathCaptor.capture(),
+ anyLong());
+ mInOrder.verify(mServiceManagerMock).registerForNotifications(
+ eq(ISupplicant.kInterfaceName), eq(""), mServiceNotificationCaptor.capture());
+ // act: cause the onRegistration(...) callback to execute
+ mServiceNotificationCaptor.getValue().onRegistration(ISupplicant.kInterfaceName, "", true);
+
+ assertTrue(mDut.isInitializationComplete() == shouldSucceed);
+ mInOrder.verify(mISupplicantMock).linkToDeath(mSupplicantDeathCaptor.capture(),
+ anyLong());
+ // verify: listInterfaces is called
+ mInOrder.verify(mISupplicantMock).listInterfaces(
+ any(ISupplicant.listInterfacesCallback.class));
+ if (!getZeroInterfaces) {
+ mInOrder.verify(mISupplicantMock)
+ .getInterface(any(ISupplicant.IfaceInfo.class),
+ any(ISupplicant.getInterfaceCallback.class));
+ }
+ if (causeRemoteException) {
+ mInOrder.verify(mWifiMonitor).broadcastSupplicantDisconnectionEvent(eq(null));
+ }
+ if (!causeRemoteException && !getZeroInterfaces && !getNullInterface) {
+ mInOrder.verify(mISupplicantStaIfaceMock).linkToDeath(
+ mSupplicantStaIfaceDeathCaptor.capture(), anyLong());
+ mInOrder.verify(mISupplicantStaIfaceMock)
+ .registerCallback(any(ISupplicantStaIfaceCallback.class));
+ }
+ }
+
+ private SupplicantStatus createSupplicantStatus(int code) {
+ SupplicantStatus status = new SupplicantStatus();
+ status.code = code;
+ return status;
+ }
+
+ /**
+ * Create an IfaceInfo with given type and name
+ */
+ private ISupplicant.IfaceInfo createIfaceInfo(int type, String name) {
+ ISupplicant.IfaceInfo info = new ISupplicant.IfaceInfo();
+ info.type = type;
+ info.name = name;
+ return info;
+ }
+
+ private class GetListInterfacesAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private ArrayList<ISupplicant.IfaceInfo> mInterfaceList;
+
+ GetListInterfacesAnswer(ArrayList<ISupplicant.IfaceInfo> ifaces) {
+ mInterfaceList = ifaces;
+ }
+
+ public void answer(ISupplicant.listInterfacesCallback cb) {
+ cb.onValues(mStatusSuccess, mInterfaceList);
+ }
+ }
+
+ private class GetGetInterfaceAnswer extends MockAnswerUtil.AnswerWithArguments {
+ boolean mGetNullInterface;
+
+ GetGetInterfaceAnswer(boolean getNullInterface) {
+ mGetNullInterface = getNullInterface;
+ }
+
+ public void answer(ISupplicant.IfaceInfo iface, ISupplicant.getInterfaceCallback cb) {
+ if (mGetNullInterface) {
+ cb.onValues(mStatusSuccess, null);
+ } else {
+ cb.onValues(mStatusSuccess, mISupplicantIfaceMock);
+ }
+ }
+ }
+
+ /**
+ * Setup mocks for connect sequence.
+ */
+ private void setupMocksForConnectSequence(final boolean haveExistingNetwork) throws Exception {
+ final int existingNetworkId = SUPPLICANT_NETWORK_ID;
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public SupplicantStatus answer() throws RemoteException {
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaIfaceMock).disconnect();
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.listNetworksCallback cb) throws RemoteException {
+ if (haveExistingNetwork) {
+ cb.onValues(mStatusSuccess, new ArrayList<>(Arrays.asList(existingNetworkId)));
+ } else {
+ cb.onValues(mStatusSuccess, new ArrayList<>());
+ }
+ }
+ }).when(mISupplicantStaIfaceMock)
+ .listNetworks(any(ISupplicantStaIface.listNetworksCallback.class));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public SupplicantStatus answer(int id) throws RemoteException {
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaIfaceMock).removeNetwork(eq(existingNetworkId));
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public void answer(ISupplicantStaIface.addNetworkCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, mock(ISupplicantStaNetwork.class));
+ return;
+ }
+ }).when(mISupplicantStaIfaceMock).addNetwork(
+ any(ISupplicantStaIface.addNetworkCallback.class));
+ when(mSupplicantStaNetworkMock.saveWifiConfiguration(any(WifiConfiguration.class)))
+ .thenReturn(true);
+ when(mSupplicantStaNetworkMock.select()).thenReturn(true);
+ }
+
+ /**
+ * Helper function to validate the connect sequence.
+ */
+ private void validateConnectSequence(
+ final boolean haveExistingNetwork, int numNetworkAdditions) throws Exception {
+ if (haveExistingNetwork) {
+ verify(mISupplicantStaIfaceMock).removeNetwork(anyInt());
+ }
+ verify(mISupplicantStaIfaceMock, times(numNetworkAdditions))
+ .addNetwork(any(ISupplicantStaIface.addNetworkCallback.class));
+ verify(mSupplicantStaNetworkMock, times(numNetworkAdditions))
+ .saveWifiConfiguration(any(WifiConfiguration.class));
+ verify(mSupplicantStaNetworkMock, times(numNetworkAdditions)).select();
+ }
+
+ /**
+ * Helper function to execute all the actions to perform connection to the network.
+ *
+ * @param newFrameworkNetworkId Framework Network Id of the new network to connect.
+ * @param haveExistingNetwork Removes the existing network.
+ * @return the WifiConfiguration object of the new network to connect.
+ */
+ private WifiConfiguration executeAndValidateConnectSequence(
+ final int newFrameworkNetworkId, final boolean haveExistingNetwork) throws Exception {
+ setupMocksForConnectSequence(haveExistingNetwork);
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = newFrameworkNetworkId;
+ assertTrue(mDut.connectToNetwork(config));
+ validateConnectSequence(haveExistingNetwork, 1);
+ return config;
+ }
+
+ /**
+ * Setup mocks for roam sequence.
+ */
+ private void setupMocksForRoamSequence(String roamBssid) throws Exception {
+ doAnswer(new MockAnswerUtil.AnswerWithArguments() {
+ public SupplicantStatus answer() throws RemoteException {
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaIfaceMock).reassociate();
+ when(mSupplicantStaNetworkMock.setBssid(eq(roamBssid))).thenReturn(true);
+ }
+
+ /**
+ * Helper function to execute all the actions to perform roaming to the network.
+ *
+ * @param sameNetwork Roam to the same network or not.
+ */
+ private void executeAndValidateRoamSequence(boolean sameNetwork) throws Exception {
+ int connectedNetworkId = ROAM_NETWORK_ID;
+ String roamBssid = BSSID;
+ int roamNetworkId;
+ if (sameNetwork) {
+ roamNetworkId = connectedNetworkId;
+ } else {
+ roamNetworkId = connectedNetworkId + 1;
+ }
+ executeAndValidateConnectSequence(connectedNetworkId, false);
+ setupMocksForRoamSequence(roamBssid);
+
+ WifiConfiguration roamingConfig = new WifiConfiguration();
+ roamingConfig.networkId = roamNetworkId;
+ roamingConfig.getNetworkSelectionStatus().setNetworkSelectionBSSID(roamBssid);
+ assertTrue(mDut.roamToNetwork(roamingConfig));
+
+ if (!sameNetwork) {
+ validateConnectSequence(false, 2);
+ verify(mSupplicantStaNetworkMock, never()).setBssid(anyString());
+ verify(mISupplicantStaIfaceMock, never()).reassociate();
+ } else {
+ verify(mSupplicantStaNetworkMock).setBssid(eq(roamBssid));
+ verify(mISupplicantStaIfaceMock).reassociate();
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java
new file mode 100644
index 0000000..6ea5c55
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalTest.java
@@ -0,0 +1,1326 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wifi;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.content.Context;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetworkCallback;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetworkCallback
+ .NetworkRequestEapSimGsmAuthParams;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetworkCallback
+ .NetworkRequestEapSimUmtsAuthParams;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.os.RemoteException;
+import android.text.TextUtils;
+
+import com.android.internal.R;
+import com.android.server.wifi.util.NativeUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * Unit tests for SupplicantStaNetworkHal
+ */
+public class SupplicantStaNetworkHalTest {
+ private static final String IFACE_NAME = "wlan0";
+ private static final Map<String, String> NETWORK_EXTRAS_VALUES = new HashMap<>();
+ static {
+ NETWORK_EXTRAS_VALUES.put("key1", "value1");
+ NETWORK_EXTRAS_VALUES.put("key2", "value2");
+ }
+ private static final String NETWORK_EXTRAS_SERIALIZED =
+ "%7B%22key1%22%3A%22value1%22%2C%22key2%22%3A%22value2%22%7D";
+ private static final String ANONYMOUS_IDENTITY = "aaa@bbb.cc.ddd";
+
+ private SupplicantStaNetworkHal mSupplicantNetwork;
+ private SupplicantStatus mStatusSuccess;
+ private SupplicantStatus mStatusFailure;
+ @Mock private ISupplicantStaNetwork mISupplicantStaNetworkMock;
+ @Mock private Context mContext;
+ @Mock private WifiMonitor mWifiMonitor;
+ private SupplicantNetworkVariables mSupplicantVariables;
+ private MockResources mResources;
+ private ISupplicantStaNetworkCallback mISupplicantStaNetworkCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mStatusSuccess = createSupplicantStatus(SupplicantStatusCode.SUCCESS);
+ mStatusFailure = createSupplicantStatus(SupplicantStatusCode.FAILURE_UNKNOWN);
+ mSupplicantVariables = new SupplicantNetworkVariables();
+ setupISupplicantNetworkMock();
+
+ mResources = new MockResources();
+ when(mContext.getResources()).thenReturn(mResources);
+ createSupplicantStaNetwork();
+ }
+
+ /**
+ * Tests the saving of WifiConfiguration to wpa_supplicant.
+ */
+ @Test
+ public void testOpenNetworkWifiConfigurationSaveLoad() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+ config.updateIdentifier = "45";
+ testWifiConfigurationSaveLoad(config);
+ }
+
+ /**
+ * Tests the saving/loading of WifiConfiguration to wpa_supplicant with psk passphrase.
+ */
+ @Test
+ public void testPskPassphraseNetworkWifiConfigurationSaveLoad() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ config.requirePMF = true;
+ testWifiConfigurationSaveLoad(config);
+ verify(mISupplicantStaNetworkMock).setPskPassphrase(anyString());
+ verify(mISupplicantStaNetworkMock)
+ .getPskPassphrase(any(ISupplicantStaNetwork.getPskPassphraseCallback.class));
+ verify(mISupplicantStaNetworkMock, never()).setPsk(any(byte[].class));
+ verify(mISupplicantStaNetworkMock, never())
+ .getPsk(any(ISupplicantStaNetwork.getPskCallback.class));
+ }
+
+ /**
+ * Tests the saving/loading of WifiConfiguration to wpa_supplicant with raw psk.
+ */
+ @Test
+ public void testPskNetworkWifiConfigurationSaveLoad() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ config.preSharedKey = "945ef00c463c2a7c2496376b13263d1531366b46377179a4b17b393687450779";
+ testWifiConfigurationSaveLoad(config);
+ verify(mISupplicantStaNetworkMock).setPsk(any(byte[].class));
+ verify(mISupplicantStaNetworkMock)
+ .getPsk(any(ISupplicantStaNetwork.getPskCallback.class));
+ verify(mISupplicantStaNetworkMock, never()).setPskPassphrase(anyString());
+ verify(mISupplicantStaNetworkMock)
+ .getPskPassphrase(any(ISupplicantStaNetwork.getPskPassphraseCallback.class));
+ }
+
+ /**
+ * Tests the saving of WifiConfiguration to wpa_supplicant removes enclosing quotes of psk
+ * passphrase
+ */
+ @Test
+ public void testPskNetworkWifiConfigurationSaveRemovesPskQuotes() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ config.preSharedKey = "\"quoted_psd\"";
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+ assertEquals(mSupplicantVariables.pskPassphrase,
+ NativeUtil.removeEnclosingQuotes(config.preSharedKey));
+ }
+
+ /**
+ * Tests the saving/loading of WifiConfiguration to wpa_supplicant.
+ */
+ @Test
+ public void testWepNetworkWifiConfigurationSaveLoad() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createWepHiddenNetwork();
+ config.BSSID = "34:45:19:09:45";
+ testWifiConfigurationSaveLoad(config);
+ }
+
+ /**
+ * Tests the saving of WifiConfiguration to wpa_supplicant.
+ */
+ @Test
+ public void testEapPeapGtcNetworkWifiConfigurationSaveLoad() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+ config.enterpriseConfig =
+ WifiConfigurationTestUtil.createPEAPWifiEnterpriseConfigWithGTCPhase2();
+ testWifiConfigurationSaveLoad(config);
+ }
+
+ /**
+ * Tests the saving of WifiConfiguration to wpa_supplicant.
+ */
+ @Test
+ public void testEapTlsNoneNetworkWifiConfigurationSaveLoad() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+ config.enterpriseConfig =
+ WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithNonePhase2();
+ testWifiConfigurationSaveLoad(config);
+ }
+
+ /**
+ * Tests the saving of WifiConfiguration to wpa_supplicant.
+ */
+ @Test
+ public void testEapTlsNoneClientCertNetworkWifiConfigurationSaveLoad() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+ config.enterpriseConfig =
+ WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithNonePhase2();
+ config.enterpriseConfig.setClientCertificateAlias("test_alias");
+ testWifiConfigurationSaveLoad(config);
+ }
+
+ /**
+ * Tests the saving of WifiConfiguration to wpa_supplicant.
+ */
+ @Test
+ public void testEapTlsAkaNetworkWifiConfigurationSaveLoad() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+ config.enterpriseConfig =
+ WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithAkaPhase2();
+ testWifiConfigurationSaveLoad(config);
+ }
+
+ /**
+ * Tests the loading of network ID.
+ */
+ @Test
+ public void testNetworkIdLoad() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createWepHiddenNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+
+ // Modify the supplicant variable directly.
+ mSupplicantVariables.networkId = 5;
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ assertTrue(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ assertEquals(mSupplicantVariables.networkId, loadConfig.networkId);
+ }
+
+ /**
+ * Tests the failure to load ssid aborts the loading of network variables.
+ */
+ @Test
+ public void testSsidLoadFailure() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createWepHiddenNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getSsidCallback cb) throws RemoteException {
+ cb.onValues(mStatusFailure, mSupplicantVariables.ssid);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getSsid(any(ISupplicantStaNetwork.getSsidCallback.class));
+
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ assertFalse(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ }
+
+ /**
+ * Tests the failure to save ssid.
+ */
+ @Test
+ public void testSsidSaveFailure() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createWepHiddenNetwork();
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(ArrayList<Byte> ssid) throws RemoteException {
+ return mStatusFailure;
+ }
+ }).when(mISupplicantStaNetworkMock).setSsid(any(ArrayList.class));
+
+ assertFalse(mSupplicantNetwork.saveWifiConfiguration(config));
+ }
+
+ /**
+ * Tests the failure to save invalid key mgmt (unknown bit set in the
+ * {@link WifiConfiguration#allowedKeyManagement} being saved).
+ */
+ @Test
+ public void testInvalidKeyMgmtSaveFailure() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createWepHiddenNetwork();
+ config.allowedKeyManagement.set(10);
+ try {
+ assertFalse(mSupplicantNetwork.saveWifiConfiguration(config));
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+ assertTrue(false);
+ }
+
+ /**
+ * Tests the failure to load invalid key mgmt (unkown bit set in the supplicant network key_mgmt
+ * variable read).
+ */
+ @Test
+ public void testInvalidKeyMgmtLoadFailure() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createWepHiddenNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+
+ // Modify the supplicant variable directly.
+ mSupplicantVariables.keyMgmtMask = 0xFFFFF;
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ try {
+ assertFalse(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+ assertTrue(false);
+ }
+
+ /**
+ * Tests the failure to save invalid bssid (less than 6 bytes in the
+ * {@link WifiConfiguration#BSSID} being saved).
+ */
+ @Test
+ public void testInvalidBssidSaveFailure() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createWepHiddenNetwork();
+ config.getNetworkSelectionStatus().setNetworkSelectionBSSID("45:34:23:12");
+ try {
+ assertFalse(mSupplicantNetwork.saveWifiConfiguration(config));
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+ assertTrue(false);
+ }
+
+ /**
+ * Tests the failure to load invalid bssid (less than 6 bytes in the supplicant bssid variable
+ * read).
+ */
+ @Test
+ public void testInvalidBssidLoadFailure() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createWepHiddenNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+
+ // Modify the supplicant variable directly.
+ mSupplicantVariables.bssid = new byte[3];
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ try {
+ assertFalse(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+ assertTrue(false);
+ }
+
+ /**
+ * Tests the loading of invalid ssid from wpa_supplicant.
+ */
+ @Test
+ public void testInvalidSsidLoadFailure() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createWepHiddenNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+
+ // Modify the supplicant variable directly.
+ mSupplicantVariables.ssid = null;
+
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ assertFalse(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ }
+
+ /**
+ * Tests the loading of invalid eap method from wpa_supplicant.
+ * Invalid eap method is assumed to be a non enterprise network. So, the loading should
+ * succeed as a non-enterprise network.
+ */
+ @Test
+ public void testInvalidEapMethodLoadFailure() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+ config.enterpriseConfig =
+ WifiConfigurationTestUtil.createPEAPWifiEnterpriseConfigWithGTCPhase2();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+
+ // Modify the supplicant variable directly.
+ mSupplicantVariables.eapMethod = -1;
+
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ assertTrue(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ }
+
+ /**
+ * Tests the loading of invalid eap phase2 method from wpa_supplicant.
+ */
+ @Test
+ public void testInvalidEapPhase2MethodLoadFailure() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+ config.enterpriseConfig =
+ WifiConfigurationTestUtil.createPEAPWifiEnterpriseConfigWithGTCPhase2();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+
+ // Modify the supplicant variable directly.
+ mSupplicantVariables.eapPhase2Method = -1;
+
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ assertFalse(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ }
+
+ /**
+ * Tests the parsing of GSM auth response parameters.
+ */
+ @Test
+ public void testSendNetworkEapSimGsmAuthResponseWith2KcSresPair() throws Exception {
+ final byte[] kc = new byte[]{0x45, 0x45, 0x32, 0x34, 0x45, 0x10, 0x34, 0x12};
+ final byte[] sres = new byte[]{0x12, 0x10, 0x32, 0x23};
+ // Send 2 kc/sres pair for this request.
+ String paramsStr = ":" + NativeUtil.hexStringFromByteArray(kc)
+ + ":" + NativeUtil.hexStringFromByteArray(sres)
+ + ":" + NativeUtil.hexStringFromByteArray(kc)
+ + ":" + NativeUtil.hexStringFromByteArray(sres);
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(
+ ArrayList<ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams> params)
+ throws RemoteException {
+ assertEquals(2, params.size());
+ assertArrayEquals(kc, params.get(0).kc);
+ assertArrayEquals(sres, params.get(0).sres);
+ assertArrayEquals(kc, params.get(1).kc);
+ assertArrayEquals(sres, params.get(1).sres);
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).sendNetworkEapSimGsmAuthResponse(any(ArrayList.class));
+
+ assertTrue(mSupplicantNetwork.sendNetworkEapSimGsmAuthResponse(paramsStr));
+ }
+
+ /**
+ * Tests the parsing of GSM auth response parameters.
+ */
+ @Test
+ public void testSendNetworkEapSimGsmAuthResponseWith3KcSresPair() throws Exception {
+ final byte[] kc1 = new byte[]{0x45, 0x45, 0x32, 0x34, 0x45, 0x10, 0x34, 0x12};
+ final byte[] sres1 = new byte[]{0x12, 0x10, 0x32, 0x23};
+ final byte[] kc2 = new byte[]{0x45, 0x34, 0x12, 0x34, 0x45, 0x10, 0x34, 0x12};
+ final byte[] sres2 = new byte[]{0x12, 0x23, 0x12, 0x23};
+ final byte[] kc3 = new byte[]{0x25, 0x34, 0x12, 0x14, 0x45, 0x10, 0x34, 0x12};
+ final byte[] sres3 = new byte[]{0x42, 0x23, 0x22, 0x23};
+ // Send 3 kc/sres pair for this request.
+ String paramsStr = ":" + NativeUtil.hexStringFromByteArray(kc1)
+ + ":" + NativeUtil.hexStringFromByteArray(sres1)
+ + ":" + NativeUtil.hexStringFromByteArray(kc2)
+ + ":" + NativeUtil.hexStringFromByteArray(sres2)
+ + ":" + NativeUtil.hexStringFromByteArray(kc3)
+ + ":" + NativeUtil.hexStringFromByteArray(sres3);
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(
+ ArrayList<ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams> params)
+ throws RemoteException {
+ assertEquals(3, params.size());
+ assertArrayEquals(kc1, params.get(0).kc);
+ assertArrayEquals(sres1, params.get(0).sres);
+ assertArrayEquals(kc2, params.get(1).kc);
+ assertArrayEquals(sres2, params.get(1).sres);
+ assertArrayEquals(kc3, params.get(2).kc);
+ assertArrayEquals(sres3, params.get(2).sres);
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).sendNetworkEapSimGsmAuthResponse(any(ArrayList.class));
+
+ assertTrue(mSupplicantNetwork.sendNetworkEapSimGsmAuthResponse(paramsStr));
+ }
+
+ /**
+ * Tests the parsing of invalid GSM auth response parameters (invalid kc & sres lengths).
+ */
+ @Test
+ public void testSendInvalidKcSresLenNetworkEapSimGsmAuthResponse() throws Exception {
+ final byte[] kc1 = new byte[]{0x45, 0x45, 0x32, 0x34, 0x45, 0x10, 0x34};
+ final byte[] sres1 = new byte[]{0x12, 0x10, 0x23};
+ final byte[] kc2 = new byte[]{0x45, 0x34, 0x12, 0x34, 0x45, 0x10, 0x34, 0x12};
+ final byte[] sres2 = new byte[]{0x12, 0x23, 0x12, 0x23};
+ // Send 2 kc/sres pair for this request.
+ String paramsStr = ":" + NativeUtil.hexStringFromByteArray(kc1)
+ + ":" + NativeUtil.hexStringFromByteArray(sres1)
+ + ":" + NativeUtil.hexStringFromByteArray(kc2)
+ + ":" + NativeUtil.hexStringFromByteArray(sres2);
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(
+ ArrayList<ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams> params)
+ throws RemoteException {
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).sendNetworkEapSimGsmAuthResponse(any(ArrayList.class));
+
+ assertFalse(mSupplicantNetwork.sendNetworkEapSimGsmAuthResponse(paramsStr));
+ }
+
+ /**
+ * Tests the parsing of invalid GSM auth response parameters (invalid number of kc/sres pairs).
+ */
+ @Test
+ public void testSendInvalidKcSresPairNumNetworkEapSimGsmAuthResponse() throws Exception {
+ final byte[] kc = new byte[]{0x45, 0x34, 0x12, 0x34, 0x45, 0x10, 0x34, 0x12};
+ final byte[] sres = new byte[]{0x12, 0x23, 0x12, 0x23};
+ // Send 1 kc/sres pair for this request.
+ String paramsStr = ":" + NativeUtil.hexStringFromByteArray(kc)
+ + ":" + NativeUtil.hexStringFromByteArray(sres);
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(
+ ArrayList<ISupplicantStaNetwork.NetworkResponseEapSimGsmAuthParams> params)
+ throws RemoteException {
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).sendNetworkEapSimGsmAuthResponse(any(ArrayList.class));
+
+ assertFalse(mSupplicantNetwork.sendNetworkEapSimGsmAuthResponse(paramsStr));
+ }
+
+ /**
+ * Tests the parsing of UMTS auth response parameters.
+ */
+ @Test
+ public void testSendNetworkEapSimUmtsAuthResponse() throws Exception {
+ final byte[] ik = new byte[]{0x45, 0x45, 0x32, 0x34, 0x45, 0x10, 0x34, 0x12, 0x23, 0x34,
+ 0x33, 0x23, 0x34, 0x10, 0x40, 0x34};
+ final byte[] ck = new byte[]{0x12, 0x10, 0x32, 0x23, 0x45, 0x10, 0x34, 0x12, 0x23, 0x34,
+ 0x33, 0x23, 0x34, 0x10, 0x40, 0x34};
+ final byte[] res = new byte[]{0x12, 0x10, 0x32, 0x23, 0x45, 0x10, 0x34, 0x12, 0x23, 0x34};
+ String paramsStr = ":" + NativeUtil.hexStringFromByteArray(ik)
+ + ":" + NativeUtil.hexStringFromByteArray(ck)
+ + ":" + NativeUtil.hexStringFromByteArray(res);
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(
+ ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams params)
+ throws RemoteException {
+ assertArrayEquals(ik, params.ik);
+ assertArrayEquals(ck, params.ck);
+ // Convert to arraylist before comparison.
+ ArrayList<Byte> resList = new ArrayList<>();
+ for (byte b : res) {
+ resList.add(b);
+ }
+ assertEquals(resList, params.res);
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).sendNetworkEapSimUmtsAuthResponse(
+ any(ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams.class));
+
+ assertTrue(mSupplicantNetwork.sendNetworkEapSimUmtsAuthResponse(paramsStr));
+ }
+
+ /**
+ * Tests the parsing of invalid UMTS auth response parameters (invalid ik, ck lengths).
+ */
+ @Test
+ public void testSendInvalidNetworkEapSimUmtsAuthResponse() throws Exception {
+ final byte[] ik = new byte[]{0x45, 0x45, 0x32, 0x34, 0x45, 0x10, 0x34, 0x12};
+ final byte[] ck = new byte[]{0x12, 0x10, 0x32, 0x23, 0x45, 0x10, 0x34, 0x12, 0x23, 0x34,
+ 0x33, 0x23, 0x34, 0x10, 0x40};
+ final byte[] res = new byte[]{0x12, 0x10, 0x32, 0x23, 0x45, 0x10, 0x34, 0x12, 0x23, 0x34};
+ String paramsStr = ":" + NativeUtil.hexStringFromByteArray(ik)
+ + ":" + NativeUtil.hexStringFromByteArray(ck)
+ + ":" + NativeUtil.hexStringFromByteArray(res);
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(
+ ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams params)
+ throws RemoteException {
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).sendNetworkEapSimUmtsAuthResponse(
+ any(ISupplicantStaNetwork.NetworkResponseEapSimUmtsAuthParams.class));
+
+ assertFalse(mSupplicantNetwork.sendNetworkEapSimUmtsAuthResponse(paramsStr));
+ }
+
+ /**
+ * Tests the parsing of UMTS auts response parameters.
+ */
+ @Test
+ public void testSendNetworkEapSimUmtsAutsResponse() throws Exception {
+ final byte[] auts = new byte[]{0x45, 0x45, 0x32, 0x34, 0x45, 0x10, 0x34, 0x12, 0x23, 0x34,
+ 0x33, 0x23, 0x34, 0x10};
+ String paramsStr = ":" + NativeUtil.hexStringFromByteArray(auts);
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(byte[] params)
+ throws RemoteException {
+ assertArrayEquals(auts, params);
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).sendNetworkEapSimUmtsAutsResponse(any(byte[].class));
+
+ assertTrue(mSupplicantNetwork.sendNetworkEapSimUmtsAutsResponse(paramsStr));
+ }
+
+ /**
+ * Tests the parsing of invalid UMTS auts response parameters (invalid auts length).
+ */
+ @Test
+ public void testSendInvalidNetworkEapSimUmtsAutsResponse() throws Exception {
+ final byte[] auts = new byte[]{0x45, 0x45, 0x32, 0x34, 0x45, 0x10, 0x34, 0x12, 0x23};
+ String paramsStr = ":" + NativeUtil.hexStringFromByteArray(auts);
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(byte[] params)
+ throws RemoteException {
+ assertArrayEquals(auts, params);
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).sendNetworkEapSimUmtsAutsResponse(any(byte[].class));
+
+ assertFalse(mSupplicantNetwork.sendNetworkEapSimUmtsAutsResponse(paramsStr));
+ }
+
+ /**
+ * Tests the parsing of identity string.
+ */
+ @Test
+ public void testSendNetworkEapIdentityResponse() throws Exception {
+ final String identityStr = "test@test.com";
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(ArrayList<Byte> identity)
+ throws RemoteException {
+ assertEquals(identityStr, NativeUtil.stringFromByteArrayList(identity));
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).sendNetworkEapIdentityResponse(any(ArrayList.class));
+
+ assertTrue(mSupplicantNetwork.sendNetworkEapIdentityResponse(identityStr));
+ }
+
+ /**
+ * Tests the addition of FT flags when the device supports it.
+ */
+ @Test
+ public void testAddFtPskFlags() throws Exception {
+ mResources.setBoolean(R.bool.config_wifi_fast_bss_transition_enabled, true);
+ createSupplicantStaNetwork();
+
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+
+ // Check the supplicant variables to ensure that we have added the FT flags.
+ assertTrue((mSupplicantVariables.keyMgmtMask & ISupplicantStaNetwork.KeyMgmtMask.FT_PSK)
+ == ISupplicantStaNetwork.KeyMgmtMask.FT_PSK);
+
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ assertTrue(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ // The FT flags should be stripped out when reading it back.
+ WifiConfigurationTestUtil.assertConfigurationEqualForSupplicant(config, loadConfig);
+ }
+
+ /**
+ * Tests the addition of FT flags when the device supports it.
+ */
+ @Test
+ public void testAddFtEapFlags() throws Exception {
+ mResources.setBoolean(R.bool.config_wifi_fast_bss_transition_enabled, true);
+ createSupplicantStaNetwork();
+
+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+
+ // Check the supplicant variables to ensure that we have added the FT flags.
+ assertTrue((mSupplicantVariables.keyMgmtMask & ISupplicantStaNetwork.KeyMgmtMask.FT_EAP)
+ == ISupplicantStaNetwork.KeyMgmtMask.FT_EAP);
+
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ assertTrue(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ // The FT flags should be stripped out when reading it back.
+ WifiConfigurationTestUtil.assertConfigurationEqualForSupplicant(config, loadConfig);
+ }
+
+ /**
+ * Tests the retrieval of WPS NFC token.
+ */
+ @Test
+ public void testGetWpsNfcConfigurationToken() throws Exception {
+ final ArrayList<Byte> token = new ArrayList<>();
+ token.add(Byte.valueOf((byte) 0x45));
+ token.add(Byte.valueOf((byte) 0x34));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getWpsNfcConfigurationTokenCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, token);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getWpsNfcConfigurationToken(
+ any(ISupplicantStaNetwork.getWpsNfcConfigurationTokenCallback.class));
+
+ assertEquals("4534", mSupplicantNetwork.getWpsNfcConfigurationToken());
+ }
+
+ /**
+ * Tests that callback registration failure triggers a failure in saving network config.
+ */
+ @Test
+ public void testSaveFailureDueToCallbackReg() throws Exception {
+ when(mISupplicantStaNetworkMock.registerCallback(any(ISupplicantStaNetworkCallback.class)))
+ .thenReturn(mStatusFailure);
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertFalse(mSupplicantNetwork.saveWifiConfiguration(config));
+ }
+
+ /**
+ * Tests the network gsm auth callback.
+ */
+ @Test
+ public void testNetworkEapGsmAuthCallback() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+ assertNotNull(mISupplicantStaNetworkCallback);
+
+ // Now trigger eap gsm callback and ensure that the event is broadcast via WifiMonitor.
+ NetworkRequestEapSimGsmAuthParams params = new NetworkRequestEapSimGsmAuthParams();
+ Random random = new Random();
+ byte[] rand1 = new byte[16];
+ byte[] rand2 = new byte[16];
+ byte[] rand3 = new byte[16];
+ random.nextBytes(rand1);
+ random.nextBytes(rand2);
+ random.nextBytes(rand3);
+ params.rands.add(rand1);
+ params.rands.add(rand2);
+ params.rands.add(rand3);
+
+ String[] expectedRands = {
+ NativeUtil.hexStringFromByteArray(rand1), NativeUtil.hexStringFromByteArray(rand2),
+ NativeUtil.hexStringFromByteArray(rand3)
+ };
+
+ mISupplicantStaNetworkCallback.onNetworkEapSimGsmAuthRequest(params);
+ verify(mWifiMonitor).broadcastNetworkGsmAuthRequestEvent(
+ eq(IFACE_NAME), eq(config.networkId), eq(config.SSID), eq(expectedRands));
+ }
+
+ /**
+ * Tests the network umts auth callback.
+ */
+ @Test
+ public void testNetworkEapUmtsAuthCallback() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+ assertNotNull(mISupplicantStaNetworkCallback);
+
+ // Now trigger eap gsm callback and ensure that the event is broadcast via WifiMonitor.
+ NetworkRequestEapSimUmtsAuthParams params = new NetworkRequestEapSimUmtsAuthParams();
+ Random random = new Random();
+ random.nextBytes(params.autn);
+ random.nextBytes(params.rand);
+
+ String[] expectedRands = {
+ NativeUtil.hexStringFromByteArray(params.rand),
+ NativeUtil.hexStringFromByteArray(params.autn)
+ };
+
+ mISupplicantStaNetworkCallback.onNetworkEapSimUmtsAuthRequest(params);
+ verify(mWifiMonitor).broadcastNetworkUmtsAuthRequestEvent(
+ eq(IFACE_NAME), eq(config.networkId), eq(config.SSID), eq(expectedRands));
+ }
+
+ /**
+ * Tests the network identity callback.
+ */
+ @Test
+ public void testNetworkIdentityCallback() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork();
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+ assertNotNull(mISupplicantStaNetworkCallback);
+
+ // Now trigger identity request callback and ensure that the event is broadcast via
+ // WifiMonitor.
+ mISupplicantStaNetworkCallback.onNetworkEapIdentityRequest();
+ verify(mWifiMonitor).broadcastNetworkIdentityRequestEvent(
+ eq(IFACE_NAME), eq(config.networkId), eq(config.SSID));
+ }
+
+ private void testWifiConfigurationSaveLoad(WifiConfiguration config) {
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+ WifiConfiguration loadConfig = new WifiConfiguration();
+ Map<String, String> networkExtras = new HashMap<>();
+ assertTrue(mSupplicantNetwork.loadWifiConfiguration(loadConfig, networkExtras));
+ WifiConfigurationTestUtil.assertConfigurationEqualForSupplicant(config, loadConfig);
+ assertEquals(config.configKey(),
+ networkExtras.get(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY));
+ assertEquals(
+ config.creatorUid,
+ Integer.parseInt(networkExtras.get(
+ SupplicantStaNetworkHal.ID_STRING_KEY_CREATOR_UID)));
+ // There is no getter for this one, so check the supplicant variable.
+ if (!TextUtils.isEmpty(config.updateIdentifier)) {
+ assertEquals(Integer.parseInt(config.updateIdentifier),
+ mSupplicantVariables.updateIdentifier);
+ }
+ // There is no getter for this one, so check the supplicant variable.
+ String oppKeyCaching =
+ config.enterpriseConfig.getFieldValue(WifiEnterpriseConfig.OPP_KEY_CACHING);
+ if (!TextUtils.isEmpty(oppKeyCaching)) {
+ assertEquals(
+ Integer.parseInt(oppKeyCaching) == 1 ? true : false,
+ mSupplicantVariables.eapProactiveKeyCaching);
+ }
+ }
+
+ /**
+ * Verifies that createNetworkExtra() & parseNetworkExtra correctly writes a serialized and
+ * URL-encoded JSON object.
+ */
+ @Test
+ public void testNetworkExtra() {
+ assertEquals(NETWORK_EXTRAS_SERIALIZED,
+ SupplicantStaNetworkHal.createNetworkExtra(NETWORK_EXTRAS_VALUES));
+ assertEquals(NETWORK_EXTRAS_VALUES,
+ SupplicantStaNetworkHal.parseNetworkExtra(NETWORK_EXTRAS_SERIALIZED));
+ }
+
+ /**
+ * Verifies that fetachEapAnonymousIdentity() can get the anonymous identity from supplicant.
+ */
+ @Test
+ public void testFetchEapAnonymousIdentity() {
+ WifiConfiguration config = WifiConfigurationTestUtil.createEapNetwork();
+ config.enterpriseConfig.setAnonymousIdentity(ANONYMOUS_IDENTITY);
+ assertTrue(mSupplicantNetwork.saveWifiConfiguration(config));
+ assertEquals(ANONYMOUS_IDENTITY, mSupplicantNetwork.fetchEapAnonymousIdentity());
+ }
+
+ /**
+ * Sets up the HIDL interface mock with all the setters/getter values.
+ * Note: This only sets up the mock to return success on all methods.
+ */
+ private void setupISupplicantNetworkMock() throws Exception {
+ /** SSID */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(ArrayList<Byte> ssid) throws RemoteException {
+ mSupplicantVariables.ssid = ssid;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setSsid(any(ArrayList.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getSsidCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.ssid);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getSsid(any(ISupplicantStaNetwork.getSsidCallback.class));
+
+ /** Network Id */
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantNetwork.getIdCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.networkId);
+ }
+ }).when(mISupplicantStaNetworkMock).getId(any(ISupplicantNetwork.getIdCallback.class));
+
+ /** BSSID */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(byte[] bssid) throws RemoteException {
+ mSupplicantVariables.bssid = bssid;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setBssid(any(byte[].class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getBssidCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.bssid);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getBssid(any(ISupplicantStaNetwork.getBssidCallback.class));
+
+ /** Scan SSID (Is Hidden Network?) */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(boolean enable) throws RemoteException {
+ mSupplicantVariables.scanSsid = enable;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setScanSsid(any(boolean.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getScanSsidCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.scanSsid);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getScanSsid(any(ISupplicantStaNetwork.getScanSsidCallback.class));
+
+ /** Require PMF*/
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(boolean enable) throws RemoteException {
+ mSupplicantVariables.requirePmf = enable;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setRequirePmf(any(boolean.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getRequirePmfCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.requirePmf);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getRequirePmf(any(ISupplicantStaNetwork.getRequirePmfCallback.class));
+
+ /** PSK passphrase */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String pskPassphrase) throws RemoteException {
+ mSupplicantVariables.pskPassphrase = pskPassphrase;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setPskPassphrase(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getPskPassphraseCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.pskPassphrase);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getPskPassphrase(any(ISupplicantStaNetwork.getPskPassphraseCallback.class));
+
+ /** PSK */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(byte[] psk) throws RemoteException {
+ mSupplicantVariables.psk = psk;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setPsk(any(byte[].class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getPskCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.psk);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getPsk(any(ISupplicantStaNetwork.getPskCallback.class));
+
+ /** WEP keys **/
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int keyIdx, ArrayList<Byte> key) throws RemoteException {
+ mSupplicantVariables.wepKey[keyIdx] = key;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setWepKey(any(int.class), any(ArrayList.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(int keyIdx, ISupplicantStaNetwork.getWepKeyCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.wepKey[keyIdx]);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getWepKey(any(int.class), any(ISupplicantStaNetwork.getWepKeyCallback.class));
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int keyIdx) throws RemoteException {
+ mSupplicantVariables.wepTxKeyIdx = keyIdx;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setWepTxKeyIdx(any(int.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getWepTxKeyIdxCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.wepTxKeyIdx);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getWepTxKeyIdx(any(ISupplicantStaNetwork.getWepTxKeyIdxCallback.class));
+
+ /** allowedKeyManagement */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int mask) throws RemoteException {
+ mSupplicantVariables.keyMgmtMask = mask;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setKeyMgmt(any(int.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getKeyMgmtCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.keyMgmtMask);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getKeyMgmt(any(ISupplicantStaNetwork.getKeyMgmtCallback.class));
+
+ /** allowedProtocols */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int mask) throws RemoteException {
+ mSupplicantVariables.protoMask = mask;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setProto(any(int.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getProtoCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.protoMask);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getProto(any(ISupplicantStaNetwork.getProtoCallback.class));
+
+ /** allowedAuthAlgorithms */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int mask) throws RemoteException {
+ mSupplicantVariables.authAlgMask = mask;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setAuthAlg(any(int.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getAuthAlgCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.authAlgMask);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getAuthAlg(any(ISupplicantStaNetwork.getAuthAlgCallback.class));
+
+ /** allowedGroupCiphers */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int mask) throws RemoteException {
+ mSupplicantVariables.groupCipherMask = mask;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setGroupCipher(any(int.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getGroupCipherCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.groupCipherMask);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getGroupCipher(any(ISupplicantStaNetwork.getGroupCipherCallback.class));
+
+ /** allowedPairwiseCiphers */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int mask) throws RemoteException {
+ mSupplicantVariables.pairwiseCipherMask = mask;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setPairwiseCipher(any(int.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getPairwiseCipherCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.pairwiseCipherMask);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getPairwiseCipher(any(ISupplicantStaNetwork.getPairwiseCipherCallback.class));
+
+ /** metadata: idstr */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String idStr) throws RemoteException {
+ mSupplicantVariables.idStr = idStr;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setIdStr(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getIdStrCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.idStr);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getIdStr(any(ISupplicantStaNetwork.getIdStrCallback.class));
+
+ /** UpdateIdentifier */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int identifier) throws RemoteException {
+ mSupplicantVariables.updateIdentifier = identifier;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setUpdateIdentifier(any(int.class));
+
+ /** EAP method */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int method) throws RemoteException {
+ mSupplicantVariables.eapMethod = method;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapMethod(any(int.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapMethodCallback cb)
+ throws RemoteException {
+ // When not set, return failure.
+ if (mSupplicantVariables.eapMethod == -1) {
+ cb.onValues(mStatusFailure, mSupplicantVariables.eapMethod);
+ } else {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapMethod);
+ }
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapMethod(any(ISupplicantStaNetwork.getEapMethodCallback.class));
+
+ /** EAP Phase 2 method */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int method) throws RemoteException {
+ mSupplicantVariables.eapPhase2Method = method;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapPhase2Method(any(int.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapPhase2MethodCallback cb)
+ throws RemoteException {
+ // When not set, return failure.
+ if (mSupplicantVariables.eapPhase2Method == -1) {
+ cb.onValues(mStatusFailure, mSupplicantVariables.eapPhase2Method);
+ } else {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapPhase2Method);
+ }
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapPhase2Method(any(ISupplicantStaNetwork.getEapPhase2MethodCallback.class));
+
+ /** EAP Identity */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(ArrayList<Byte> identity) throws RemoteException {
+ mSupplicantVariables.eapIdentity = identity;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapIdentity(any(ArrayList.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapIdentityCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapIdentity);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapIdentity(any(ISupplicantStaNetwork.getEapIdentityCallback.class));
+
+ /** EAP Anonymous Identity */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(ArrayList<Byte> identity) throws RemoteException {
+ mSupplicantVariables.eapAnonymousIdentity = identity;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapAnonymousIdentity(any(ArrayList.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapAnonymousIdentityCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapAnonymousIdentity);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapAnonymousIdentity(
+ any(ISupplicantStaNetwork.getEapAnonymousIdentityCallback.class));
+
+ /** EAP Password */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(ArrayList<Byte> password) throws RemoteException {
+ mSupplicantVariables.eapPassword = password;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapPassword(any(ArrayList.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapPasswordCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapPassword);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapPassword(any(ISupplicantStaNetwork.getEapPasswordCallback.class));
+
+ /** EAP Client Cert */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String cert) throws RemoteException {
+ mSupplicantVariables.eapClientCert = cert;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapClientCert(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapClientCertCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapClientCert);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapClientCert(any(ISupplicantStaNetwork.getEapClientCertCallback.class));
+
+ /** EAP CA Cert */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String cert) throws RemoteException {
+ mSupplicantVariables.eapCACert = cert;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapCACert(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapCACertCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapCACert);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapCACert(any(ISupplicantStaNetwork.getEapCACertCallback.class));
+
+ /** EAP Subject Match */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String match) throws RemoteException {
+ mSupplicantVariables.eapSubjectMatch = match;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapSubjectMatch(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapSubjectMatchCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapSubjectMatch);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapSubjectMatch(any(ISupplicantStaNetwork.getEapSubjectMatchCallback.class));
+
+ /** EAP Engine */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(boolean enable) throws RemoteException {
+ mSupplicantVariables.eapEngine = enable;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapEngine(any(boolean.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapEngineCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapEngine);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapEngine(any(ISupplicantStaNetwork.getEapEngineCallback.class));
+
+ /** EAP Engine ID */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String id) throws RemoteException {
+ mSupplicantVariables.eapEngineID = id;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapEngineID(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapEngineIDCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapEngineID);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapEngineID(any(ISupplicantStaNetwork.getEapEngineIDCallback.class));
+
+ /** EAP Private Key */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String key) throws RemoteException {
+ mSupplicantVariables.eapPrivateKeyId = key;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapPrivateKeyId(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapPrivateKeyIdCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapPrivateKeyId);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapPrivateKeyId(any(ISupplicantStaNetwork.getEapPrivateKeyIdCallback.class));
+
+ /** EAP Alt Subject Match */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String match) throws RemoteException {
+ mSupplicantVariables.eapAltSubjectMatch = match;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapAltSubjectMatch(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapAltSubjectMatchCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapAltSubjectMatch);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapAltSubjectMatch(
+ any(ISupplicantStaNetwork.getEapAltSubjectMatchCallback.class));
+
+ /** EAP Domain Suffix Match */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String match) throws RemoteException {
+ mSupplicantVariables.eapDomainSuffixMatch = match;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapDomainSuffixMatch(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapDomainSuffixMatchCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapDomainSuffixMatch);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapDomainSuffixMatch(
+ any(ISupplicantStaNetwork.getEapDomainSuffixMatchCallback.class));
+
+ /** EAP CA Path*/
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(String path) throws RemoteException {
+ mSupplicantVariables.eapCAPath = path;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setEapCAPath(any(String.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantStaNetwork.getEapCAPathCallback cb)
+ throws RemoteException {
+ cb.onValues(mStatusSuccess, mSupplicantVariables.eapCAPath);
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .getEapCAPath(any(ISupplicantStaNetwork.getEapCAPathCallback.class));
+
+ /** EAP Proactive Key Caching */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(boolean enable) throws RemoteException {
+ mSupplicantVariables.eapProactiveKeyCaching = enable;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock).setProactiveKeyCaching(any(boolean.class));
+
+ /** Callback registeration */
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(ISupplicantStaNetworkCallback cb)
+ throws RemoteException {
+ mISupplicantStaNetworkCallback = cb;
+ return mStatusSuccess;
+ }
+ }).when(mISupplicantStaNetworkMock)
+ .registerCallback(any(ISupplicantStaNetworkCallback.class));
+ }
+
+ private SupplicantStatus createSupplicantStatus(int code) {
+ SupplicantStatus status = new SupplicantStatus();
+ status.code = code;
+ return status;
+ }
+
+ /**
+ * Need this for tests which wants to manipulate context before creating the instance.
+ */
+ private void createSupplicantStaNetwork() {
+ mSupplicantNetwork =
+ new SupplicantStaNetworkHal(
+ mISupplicantStaNetworkMock, IFACE_NAME, mContext, mWifiMonitor);
+ }
+
+ // Private class to to store/inspect values set via the HIDL mock.
+ private class SupplicantNetworkVariables {
+ public ArrayList<Byte> ssid;
+ public int networkId;
+ public byte[/* 6 */] bssid;
+ public int keyMgmtMask;
+ public int protoMask;
+ public int authAlgMask;
+ public int groupCipherMask;
+ public int pairwiseCipherMask;
+ public boolean scanSsid;
+ public boolean requirePmf;
+ public String idStr;
+ public int updateIdentifier;
+ public String pskPassphrase;
+ public byte[] psk;
+ public ArrayList<Byte>[] wepKey = new ArrayList[4];
+ public int wepTxKeyIdx;
+ public int eapMethod = -1;
+ public int eapPhase2Method = -1;
+ public ArrayList<Byte> eapIdentity;
+ public ArrayList<Byte> eapAnonymousIdentity;
+ public ArrayList<Byte> eapPassword;
+ public String eapCACert;
+ public String eapCAPath;
+ public String eapClientCert;
+ public String eapPrivateKeyId;
+ public String eapSubjectMatch;
+ public String eapAltSubjectMatch;
+ public boolean eapEngine;
+ public String eapEngineID;
+ public String eapDomainSuffixMatch;
+ public boolean eapProactiveKeyCaching;
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/SupplicantStateTrackerTest.java b/tests/wifitests/src/com/android/server/wifi/SupplicantStateTrackerTest.java
new file mode 100644
index 0000000..98fa800
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/SupplicantStateTrackerTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.*;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.os.Message;
+import android.os.test.TestLooper;
+
+import com.android.internal.app.IBatteryStats;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+/**
+ * Unit tests for {@link android.net.wifi.SupplicantStateTracker}.
+ */
+public class SupplicantStateTrackerTest {
+
+ private static final String TAG = "SupplicantStateTrackerTest";
+ private static final String sSSID = "\"GoogleGuest\"";
+ private static final WifiSsid sWifiSsid = WifiSsid.createFromAsciiEncoded(sSSID);
+ private static final String sBSSID = "01:02:03:04:05:06";
+
+ private @Mock WifiConfigManager mWcm;
+ private @Mock Context mContext;
+ private Handler mHandler;
+ private SupplicantStateTracker mSupplicantStateTracker;
+ private TestLooper mLooper;
+ private FrameworkFacade mFacade;
+
+ private FrameworkFacade getFrameworkFacade() {
+ FrameworkFacade facade = mock(FrameworkFacade.class);
+ IBatteryStats batteryStatsService = mock(IBatteryStats.class);
+ when(facade.getBatteryService()).thenReturn(batteryStatsService);
+ return facade;
+ }
+
+ private Message getSupplicantStateChangeMessage(int networkId, WifiSsid wifiSsid,
+ String bssid, SupplicantState newSupplicantState) {
+ return Message.obtain(null, WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+ new StateChangeResult(networkId, wifiSsid, bssid, newSupplicantState));
+
+ }
+
+ @Before
+ public void setUp() {
+ mLooper = new TestLooper();
+ mHandler = new Handler(mLooper.getLooper());
+ MockitoAnnotations.initMocks(this);
+ mFacade = getFrameworkFacade();
+ mSupplicantStateTracker = new SupplicantStateTracker(mContext, mWcm, mFacade, mHandler);
+ }
+
+ /**
+ * This test verifies that the SupplicantStateTracker sends a broadcast intent upon receiving
+ * a message when supplicant state changes
+ */
+ @Test
+ public void testSupplicantStateChangeIntent() {
+ BroadcastReceiver wifiBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ assertTrue(action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION));
+ SupplicantState recvdState =
+ (SupplicantState) intent.getExtra(WifiManager.EXTRA_NEW_STATE, -1);
+ assertEquals(SupplicantState.SCANNING, recvdState);
+ }
+ };
+ IntentFilter mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(wifiBroadcastReceiver, mIntentFilter);
+ mSupplicantStateTracker.sendMessage(getSupplicantStateChangeMessage(0, sWifiSsid,
+ sBSSID, SupplicantState.SCANNING));
+ }
+
+ /**
+ * This test verifies that the current auth status is sent in the Broadcast intent
+ */
+ @Test
+ public void testAuthPassInSupplicantStateChangeIntent() {
+ BroadcastReceiver wifiBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ assertTrue(action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION));
+ SupplicantState recvdState =
+ (SupplicantState) intent.getExtra(WifiManager.EXTRA_NEW_STATE, -1);
+ assertEquals(SupplicantState.AUTHENTICATING, recvdState);
+ boolean authStatus =
+ (boolean) intent.getExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
+ assertEquals(authStatus, true);
+ }
+ };
+ IntentFilter mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(wifiBroadcastReceiver, mIntentFilter);
+ mSupplicantStateTracker.sendMessage(getSupplicantStateChangeMessage(0, sWifiSsid,
+ sBSSID, SupplicantState.AUTHENTICATING));
+ mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
+ }
+
+ /**
+ * This test verifies that the current auth status is sent in the Broadcast intent
+ */
+ @Test
+ public void testAuthFailedInSupplicantStateChangeIntent() {
+ BroadcastReceiver wifiBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ assertTrue(action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION));
+ SupplicantState recvdState =
+ (SupplicantState) intent.getExtra(WifiManager.EXTRA_NEW_STATE, -1);
+ assertEquals(SupplicantState.AUTHENTICATING, recvdState);
+ boolean authStatus =
+ (boolean) intent.getExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
+ assertEquals(authStatus, false);
+ }
+ };
+ IntentFilter mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(wifiBroadcastReceiver, mIntentFilter);
+ mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT);
+ mSupplicantStateTracker.sendMessage(getSupplicantStateChangeMessage(0, sWifiSsid,
+ sBSSID, SupplicantState.AUTHENTICATING));
+ }
+
+ /**
+ * This test verifies the correct reasonCode for auth failure is sent in Broadcast
+ * intent.
+ */
+ @Test
+ public void testReasonCodeInSupplicantStateChangeIntent() {
+ BroadcastReceiver wifiBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ assertTrue(action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION));
+ SupplicantState recvdState =
+ (SupplicantState) intent.getExtra(WifiManager.EXTRA_NEW_STATE, -1);
+ assertEquals(SupplicantState.AUTHENTICATING, recvdState);
+ boolean authStatus =
+ (boolean) intent.getExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
+ assertEquals(authStatus, false);
+ int reasonCode = (int)
+ intent.getExtra(WifiManager.EXTRA_SUPPLICANT_ERROR_REASON, -1);
+ assertEquals(reasonCode, WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
+ }
+ };
+ IntentFilter mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(wifiBroadcastReceiver, mIntentFilter);
+ mSupplicantStateTracker.sendMessage(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0,
+ WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD);
+ mSupplicantStateTracker.sendMessage(getSupplicantStateChangeMessage(0, sWifiSsid,
+ sBSSID, SupplicantState.AUTHENTICATING));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/TestUtil.java b/tests/wifitests/src/com/android/server/wifi/TestUtil.java
index 453cfa8..a0a1030 100644
--- a/tests/wifitests/src/com/android/server/wifi/TestUtil.java
+++ b/tests/wifitests/src/com/android/server/wifi/TestUtil.java
@@ -16,8 +16,6 @@
package com.android.server.wifi;
-import static org.mockito.Mockito.when;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -25,25 +23,12 @@
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
-import java.lang.reflect.Field;
import java.util.ArrayList;
/**
* Utils for wifi tests.
*/
public class TestUtil {
-
- /**
- * Override wifi interface using {@code wifiNative}.
- */
- public static void installWlanWifiNative(WifiNative wifiNative) throws Exception {
- Field field = WifiNative.class.getDeclaredField("wlanNativeInterface");
- field.setAccessible(true);
- field.set(null, wifiNative);
-
- when(wifiNative.getInterfaceName()).thenReturn("mockWlan");
- }
-
/**
* Send {@link WifiManager#NETWORK_STATE_CHANGED_ACTION} broadcast.
*/
@@ -86,6 +71,25 @@
}
/**
+ * Send {@link WifiManager#WIFI_AP_STATE_CHANGED} broadcast.
+ */
+ public static void sendWifiApStateChanged(BroadcastReceiver broadcastReceiver,
+ Context context, int apState, int previousState, int error, String ifaceName,
+ int mode) {
+ Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, apState);
+ intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousState);
+ if (apState == WifiManager.WIFI_AP_STATE_FAILED) {
+ // only set reason number when softAP start failed
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, error);
+ }
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, ifaceName);
+ intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mode);
+
+ broadcastReceiver.onReceive(context, intent);
+ }
+
+ /**
* Send {@link ConnectivityManager#ACTION_TETHER_STATE_CHANGED} broadcast.
*/
public static void sendTetherStateChanged(BroadcastReceiver broadcastReceiver,
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
index a942a08..2d3b066 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java
@@ -49,6 +49,9 @@
private static final String TEST_DEFAULT_2G_CHANNEL_LIST = "1,2,3,4,5,6";
private static final String TEST_DEFAULT_AP_SSID = "TestAP";
private static final String TEST_CONFIGURED_AP_SSID = "ConfiguredAP";
+ private static final String TEST_DEFAULT_HOTSPOT_SSID = "TestShare";
+ private static final int RAND_SSID_INT_MIN = 1000;
+ private static final int RAND_SSID_INT_MAX = 9999;
@Mock Context mContext;
@Mock BackupManagerProxy mBackupManagerProxy;
@@ -67,6 +70,8 @@
TEST_DEFAULT_2G_CHANNEL_LIST);
resources.setString(R.string.wifi_tether_configure_ssid_default,
TEST_DEFAULT_AP_SSID);
+ resources.setString(R.string.wifi_localhotspot_configure_ssid_default,
+ TEST_DEFAULT_HOTSPOT_SSID);
when(mContext.getResources()).thenReturn(resources);
}
@@ -105,8 +110,12 @@
assertEquals(config1.apChannel, config2.apChannel);
}
- private void verifyDefaultApConfig(WifiConfiguration config) {
- assertEquals(TEST_DEFAULT_AP_SSID, config.SSID);
+ private void verifyDefaultApConfig(WifiConfiguration config, String expectedSsid) {
+ String[] splitSsid = config.SSID.split("_");
+ assertEquals(2, splitSsid.length);
+ assertEquals(expectedSsid, splitSsid[0]);
+ int randomPortion = Integer.parseInt(splitSsid[1]);
+ assertTrue(randomPortion >= RAND_SSID_INT_MIN && randomPortion <= RAND_SSID_INT_MAX);
assertTrue(config.allowedKeyManagement.get(KeyMgmt.WPA2_PSK));
}
@@ -118,7 +127,7 @@
public void initWithDefaultConfiguration() throws Exception {
WifiApConfigStore store = new WifiApConfigStore(
mContext, mBackupManagerProxy, mApConfigFile.getPath());
- verifyDefaultApConfig(store.getApConfiguration());
+ verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
}
/**
@@ -159,7 +168,7 @@
verifyApConfig(expectedConfig, store.getApConfiguration());
store.setApConfiguration(null);
- verifyDefaultApConfig(store.getApConfiguration());
+ verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
verify(mBackupManagerProxy).notifyDataChanged();
}
@@ -171,7 +180,7 @@
/* Initialize WifiApConfigStore with default configuration. */
WifiApConfigStore store = new WifiApConfigStore(
mContext, mBackupManagerProxy, mApConfigFile.getPath());
- verifyDefaultApConfig(store.getApConfiguration());
+ verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID);
/* Update with a valid configuration. */
WifiConfiguration expectedConfig = setupApConfig(
@@ -184,4 +193,16 @@
verifyApConfig(expectedConfig, store.getApConfiguration());
verify(mBackupManagerProxy).notifyDataChanged();
}
+
+ /**
+ * Verify a proper local only hotspot config is generated when called properly with the valid
+ * context.
+ */
+ @Test
+ public void generateLocalOnlyHotspotConfigIsValid() {
+ WifiConfiguration config = WifiApConfigStore.generateLocalOnlyHotspotConfig(mContext);
+ verifyDefaultApConfig(config, TEST_DEFAULT_HOTSPOT_SSID);
+ // The LOHS config should also have a specific network id set - check that as well.
+ assertEquals(WifiConfiguration.LOCAL_ONLY_NETWORK_ID, config.networkId);
+ }
}
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..58b8d39
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiBackupRestoreTest.java
@@ -0,0 +1,791 @@
+/*
+ * 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.os.Process;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.net.IpConfigStore;
+import com.android.server.wifi.util.WifiPermissionsUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+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 {
+
+ @Mock WifiPermissionsUtil mWifiPermissionsUtil;
+ private WifiBackupRestore mWifiBackupRestore;
+ private boolean mCheckDump = true;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ mWifiBackupRestore = new WifiBackupRestore(mWifiPermissionsUtil);
+ // 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 null network list is serialized correctly.
+ */
+ @Test
+ public void testNullNetworkListBackup() {
+ byte[] backupData = mWifiBackupRestore.retrieveBackupDataFromConfigurations(null);
+ assertTrue(backupData != null);
+ assertEquals(backupData.length, 0);
+ // No valid data to check in dump.
+ mCheckDump = false;
+ }
+
+ /**
+ * 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 multiple networks of different types except the non system app created ones are
+ * serialized and deserialized correctly.
+ */
+ @Test
+ public void testMultipleNetworksSystemAppBackupRestore() {
+ int systemAppUid = Process.SYSTEM_UID;
+ int nonSystemAppUid = Process.FIRST_APPLICATION_UID + 556;
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(eq(systemAppUid)))
+ .thenReturn(true);
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(eq(nonSystemAppUid)))
+ .thenReturn(false);
+
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ List<WifiConfiguration> expectedConfigurations = new ArrayList<>();
+
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ wepNetwork.creatorUid = systemAppUid;
+ configurations.add(wepNetwork);
+ expectedConfigurations.add(wepNetwork);
+
+ // These should not be in |expectedConfigurations|.
+ WifiConfiguration nonSystemAppWepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ nonSystemAppWepNetwork.creatorUid = nonSystemAppUid;
+ configurations.add(nonSystemAppWepNetwork);
+
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ pskNetwork.creatorUid = systemAppUid;
+ configurations.add(pskNetwork);
+ expectedConfigurations.add(pskNetwork);
+
+ // These should not be in |expectedConfigurations|.
+ WifiConfiguration nonSystemAppPskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ nonSystemAppPskNetwork.creatorUid = nonSystemAppUid;
+ configurations.add(nonSystemAppPskNetwork);
+
+ 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 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 multiple networks of different types except the non system app created ones are
+ * serialized and deserialized correctly from old backups.
+ */
+ @Test
+ public void testMultipleNetworksSystemAppSupplicantBackupRestore() {
+ List<WifiConfiguration> configurations = new ArrayList<>();
+ List<WifiConfiguration> expectedConfigurations = new ArrayList<>();
+
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ configurations.add(wepNetwork);
+ expectedConfigurations.add(wepNetwork);
+
+ // These should not be in |expectedConfigurations|.
+ WifiConfiguration nonSystemAppWepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ nonSystemAppWepNetwork.creatorUid = Process.FIRST_APPLICATION_UID;
+ configurations.add(nonSystemAppWepNetwork);
+
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ configurations.add(pskNetwork);
+ expectedConfigurations.add(pskNetwork);
+
+ // These should not be in |expectedConfigurations|.
+ WifiConfiguration nonSystemAppPskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ nonSystemAppPskNetwork.creatorUid = Process.FIRST_APPLICATION_UID + 1;
+ configurations.add(nonSystemAppPskNetwork);
+
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ configurations.add(openNetwork);
+ expectedConfigurations.add(openNetwork);
+
+ byte[] supplicantData = createWpaSupplicantConfBackupData(configurations);
+ byte[] ipConfigData = createIpConfBackupData(configurations);
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, ipConfigData);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ expectedConfigurations, retrievedConfigurations);
+ }
+
+ /**
+ * Verifying that backup data containing some unknown keys is properly restored.
+ * The backup data used here is a PII masked version of a backup data seen in a reported bug.
+ */
+ @Test
+ public void testSingleNetworkSupplicantBackupRestoreWithUnknownEAPKey() {
+ String backupSupplicantConfNetworkBlock = "network={\n"
+ + "ssid=" + WifiConfigurationTestUtil.TEST_SSID + "\n"
+ + "psk=" + WifiConfigurationTestUtil.TEST_PSK + "\n"
+ + "key_mgmt=WPA-PSK WPA-PSK-SHA256\n"
+ + "priority=18\n"
+ + "id_str=\"%7B%22creatorUid%22%3A%221000%22%2C%22configKey"
+ + "%22%3A%22%5C%22BLAH%5C%22WPA_PSK%22%7D\"\n"
+ + "eapRetryCount=6\n";
+ byte[] supplicantData = backupSupplicantConfNetworkBlock.getBytes();
+ List<WifiConfiguration> retrievedConfigurations =
+ mWifiBackupRestore.retrieveConfigurationsFromSupplicantBackupData(
+ supplicantData, null);
+
+ final WifiConfiguration expectedConfiguration = new WifiConfiguration();
+ expectedConfiguration.SSID = WifiConfigurationTestUtil.TEST_SSID;
+ expectedConfiguration.preSharedKey = WifiConfigurationTestUtil.TEST_PSK;
+ expectedConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+
+ ArrayList<WifiConfiguration> expectedConfigurations = new ArrayList<WifiConfiguration>() {{
+ add(expectedConfiguration);
+ }};
+ WifiConfigurationTestUtil.assertConfigurationsEqualForBackup(
+ expectedConfigurations, 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(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY, configuration.configKey());
+ extras.put(SupplicantStaNetworkHal.ID_STRING_KEY_CREATOR_UID,
+ Integer.toString(configuration.creatorUid));
+ String idString = "\"" + SupplicantStaNetworkHal.createNetworkExtra(extras) + "\"";
+ if (idString != null) {
+ 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/WifiConfigManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
index fab06bd..9fa67a0 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java
@@ -11,1627 +11,3930 @@
* 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;
-import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-import android.net.wifi.FakeKeys;
+import android.net.IpConfiguration;
+import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.AuthAlgorithm;
-import android.net.wifi.WifiConfiguration.GroupCipher;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WifiConfiguration.PairwiseCipher;
-import android.net.wifi.WifiConfiguration.Protocol;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.net.wifi.WifiEnterpriseConfig;
-import android.net.wifi.WifiEnterpriseConfig.Eap;
-import android.net.wifi.WifiEnterpriseConfig.Phase2;
+import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
+import android.net.wifi.WifiSsid;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
-import android.security.Credentials;
-import android.security.KeyStore;
-import android.support.test.InstrumentationRegistry;
+import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
+import android.util.Pair;
-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;
+import com.android.internal.R;
+import com.android.server.wifi.WifiConfigStoreLegacy.WifiConfigStoreDataLegacy;
+import com.android.server.wifi.util.WifiPermissionsUtil;
+import com.android.server.wifi.util.WifiPermissionsWrapper;
+import org.junit.After;
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.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.math.BigInteger;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayDeque;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.BitSet;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Random;
import java.util.Set;
-import java.util.TreeMap;
/**
* Unit tests for {@link com.android.server.wifi.WifiConfigManager}.
*/
@SmallTest
public class WifiConfigManagerTest {
- private static final List<WifiConfiguration> CONFIGS = Arrays.asList(
- WifiConfigurationTestUtil.generateWifiConfig(
- 0, 1000000, "\"red\"", true, true, null, null),
- WifiConfigurationTestUtil.generateWifiConfig(
- 1, 1000001, "\"green\"", true, true, "example.com", "Green"),
- WifiConfigurationTestUtil.generateWifiConfig(
- 2, 1100000, "\"blue\"", false, true, "example.org", "Blue"),
- WifiConfigurationTestUtil.generateWifiConfig(
- 3, 1200000, "\"cyan\"", false, true, null, null));
- private static final int[] USER_IDS = {0, 10, 11};
- private static final int MANAGED_PROFILE_USER_ID = 12;
- private static final int MANAGED_PROFILE_PARENT_USER_ID = 0;
- private static final SparseArray<List<UserInfo>> USER_PROFILES = new SparseArray<>();
- static {
- USER_PROFILES.put(0, Arrays.asList(new UserInfo(0, "Owner", 0),
- new UserInfo(12, "Managed Profile", 0)));
- USER_PROFILES.put(10, Arrays.asList(new UserInfo(10, "Alice", 0)));
- USER_PROFILES.put(11, Arrays.asList(new UserInfo(11, "Bob", 0)));
- }
+ 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 = WifiConfigurationTestUtil.TEST_UID;
+ private static final int TEST_NO_PERM_UID = 7;
+ private static final int TEST_UPDATE_UID = 4;
+ private static final int TEST_SYSUI_UID = 56;
+ private static final int TEST_DEFAULT_USER = UserHandle.USER_SYSTEM;
+ private static final int TEST_MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCAN = 5;
+ private static final Integer[] TEST_FREQ_LIST = {2400, 2450, 5150, 5175, 5650};
+ private static final String TEST_CREATOR_NAME = "com.wificonfigmanager.creator";
+ private static final String TEST_UPDATE_NAME = "com.wificonfigmanager.update";
+ private static final String TEST_NO_PERM_NAME = "com.wificonfigmanager.noperm";
+ private static final String TEST_DEFAULT_GW_MAC_ADDRESS = "0f:67:ad:ef:09:34";
+ private static final String TEST_STATIC_PROXY_HOST_1 = "192.168.48.1";
+ private static final int TEST_STATIC_PROXY_PORT_1 = 8000;
+ private static final String TEST_STATIC_PROXY_EXCLUSION_LIST_1 = "";
+ private static final String TEST_PAC_PROXY_LOCATION_1 = "http://bleh";
+ private static final String TEST_STATIC_PROXY_HOST_2 = "192.168.1.1";
+ private static final int TEST_STATIC_PROXY_PORT_2 = 3000;
+ private static final String TEST_STATIC_PROXY_EXCLUSION_LIST_2 = "";
+ private static final String TEST_PAC_PROXY_LOCATION_2 = "http://blah";
- private static final Map<Integer, List<WifiConfiguration>> VISIBLE_CONFIGS = new HashMap<>();
- static {
- for (int userId : USER_IDS) {
- List<WifiConfiguration> configs = new ArrayList<>();
- for (int i = 0; i < CONFIGS.size(); ++i) {
- if (WifiConfigurationUtil.isVisibleToAnyProfile(CONFIGS.get(i),
- USER_PROFILES.get(userId))) {
- configs.add(CONFIGS.get(i));
- }
- }
- VISIBLE_CONFIGS.put(userId, configs);
- }
- }
-
- /**
- * Set of WifiConfigs for HasEverConnected tests.
- */
- private static final int HAS_EVER_CONNECTED_USER = 20;
- private static final WifiConfiguration BASE_HAS_EVER_CONNECTED_CONFIG =
- WifiConfigurationTestUtil.generateWifiConfig(
- 0, HAS_EVER_CONNECTED_USER, "testHasEverConnected", false, true, null, null, 0);
-
- public static final String TAG = "WifiConfigManagerTest";
@Mock private Context mContext;
- @Mock private WifiNative mWifiNative;
- @Mock private FrameworkFacade mFrameworkFacade;
- @Mock private UserManager mUserManager;
- @Mock private DelayedDiskWrite mWriter;
- @Mock private PasspointManagementObjectManager mMOManager;
@Mock private Clock mClock;
+ @Mock private UserManager mUserManager;
+ @Mock private TelephonyManager mTelephonyManager;
+ @Mock private WifiKeyStore mWifiKeyStore;
+ @Mock private WifiConfigStore mWifiConfigStore;
+ @Mock private WifiConfigStoreLegacy mWifiConfigStoreLegacy;
+ @Mock private PackageManager mPackageManager;
+ @Mock private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
+ @Mock private WifiPermissionsUtil mWifiPermissionsUtil;
+ @Mock private WifiPermissionsWrapper mWifiPermissionsWrapper;
+ @Mock private NetworkListStoreData mNetworkListStoreData;
+ @Mock private DeletedEphemeralSsidsStoreData mDeletedEphemeralSsidsStoreData;
+ @Mock private WifiConfigManager.OnSavedNetworkUpdateListener mWcmListener;
+
+ private MockResources mResources;
+ private InOrder mContextConfigStoreMockOrder;
+ private InOrder mNetworkListStoreDataMockOrder;
private WifiConfigManager mWifiConfigManager;
- private ConfigurationMap mConfiguredNetworks;
- public byte[] mNetworkHistoryBytes;
- private MockKeyStore mMockKeyStore;
- private KeyStore mKeyStore;
+ private boolean mStoreReadTriggered = false;
/**
- * Called before each test
+ * Setup the mocks and an instance of WifiConfigManager before each test.
*/
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- final Context realContext = InstrumentationRegistry.getContext();
- when(mContext.getPackageName()).thenReturn(realContext.getPackageName());
- when(mContext.getResources()).thenReturn(realContext.getResources());
- when(mContext.getPackageManager()).thenReturn(realContext.getPackageManager());
+ // 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);
+ mNetworkListStoreDataMockOrder = inOrder(mNetworkListStoreData);
- when(mUserManager.getProfiles(UserHandle.USER_SYSTEM))
- .thenReturn(USER_PROFILES.get(UserHandle.USER_SYSTEM));
+ // Set up the package name stuff & permission override.
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ mResources = new MockResources();
+ mResources.setBoolean(
+ R.bool.config_wifi_only_link_same_credential_configurations, true);
+ mResources.setInteger(
+ R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels,
+ TEST_MAX_NUM_ACTIVE_CHANNELS_FOR_PARTIAL_SCAN);
+ when(mContext.getResources()).thenReturn(mResources);
- for (int userId : USER_IDS) {
- when(mUserManager.getProfiles(userId)).thenReturn(USER_PROFILES.get(userId));
- }
+ // Setup UserManager profiles for the default user.
+ setupUserProfiles(TEST_DEFAULT_USER);
- mMockKeyStore = new MockKeyStore();
-
- mWifiConfigManager = new WifiConfigManager(mContext, mWifiNative, mFrameworkFacade, mClock,
- mUserManager, mMockKeyStore.createMock());
-
- final Field configuredNetworksField =
- WifiConfigManager.class.getDeclaredField("mConfiguredNetworks");
- configuredNetworksField.setAccessible(true);
- mConfiguredNetworks = (ConfigurationMap) configuredNetworksField.get(mWifiConfigManager);
-
- // Intercept writes to networkHistory.txt.
doAnswer(new AnswerWithArguments() {
- public void answer(String filePath, DelayedDiskWrite.Writer writer) throws Exception {
- final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- final DataOutputStream stream = new DataOutputStream(buffer);
- writer.onWriteCalled(stream);
- stream.close();
- mNetworkHistoryBytes = buffer.toByteArray();
- }}).when(mWriter).write(anyString(), (DelayedDiskWrite.Writer) anyObject());
- final Field writerField = WifiConfigManager.class.getDeclaredField("mWriter");
- writerField.setAccessible(true);
- writerField.set(mWifiConfigManager, mWriter);
- final Field networkHistoryField =
- WifiConfigManager.class.getDeclaredField("mWifiNetworkHistory");
- networkHistoryField.setAccessible(true);
- WifiNetworkHistory wifiNetworkHistory =
- (WifiNetworkHistory) networkHistoryField.get(mWifiConfigManager);
- final Field networkHistoryWriterField =
- WifiNetworkHistory.class.getDeclaredField("mWriter");
- networkHistoryWriterField.setAccessible(true);
- networkHistoryWriterField.set(wifiNetworkHistory, mWriter);
+ 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;
+ } else if (uid == TEST_SYSUI_UID) {
+ return WifiConfigManager.SYSUI_PACKAGE_NAME;
+ } else if (uid == TEST_NO_PERM_UID) {
+ return TEST_NO_PERM_NAME;
+ }
+ fail("Unexpected UID: " + uid);
+ return "";
+ }
+ }).when(mPackageManager).getNameForUid(anyInt());
+ doAnswer(new AnswerWithArguments() {
+ public int answer(String packageName, int flags, int userId) throws Exception {
+ if (packageName.equals(WifiConfigManager.SYSUI_PACKAGE_NAME)) {
+ return TEST_SYSUI_UID;
+ } else {
+ return 0;
+ }
+ }
+ }).when(mPackageManager).getPackageUidAsUser(anyString(), anyInt(), anyInt());
- when(mMOManager.isEnabled()).thenReturn(true);
- final Field moManagerField = WifiConfigManager.class.getDeclaredField("mMOManager");
- moManagerField.setAccessible(true);
- moManagerField.set(mWifiConfigManager, mMOManager);
- }
-
- private void switchUser(int newUserId) {
- when(mUserManager.getProfiles(newUserId))
- .thenReturn(USER_PROFILES.get(newUserId));
- mWifiConfigManager.handleUserSwitch(newUserId);
- }
-
- private void switchUserToCreatorOrParentOf(WifiConfiguration config) {
- final int creatorUserId = UserHandle.getUserId(config.creatorUid);
- if (creatorUserId == MANAGED_PROFILE_USER_ID) {
- switchUser(MANAGED_PROFILE_PARENT_USER_ID);
- } else {
- switchUser(creatorUserId);
- }
- }
-
- private void addNetworks() throws Exception {
- for (int i = 0; i < CONFIGS.size(); ++i) {
- assertEquals(i, CONFIGS.get(i).networkId);
- addNetwork(CONFIGS.get(i));
- }
- }
-
- private void addNetwork(WifiConfiguration config) throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
-
- when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true);
- when(mWifiNative.setNetworkExtra(anyInt(), anyString(), (Map<String, String>) anyObject()))
+ when(mWifiKeyStore
+ .updateNetworkKeys(any(WifiConfiguration.class), any()))
.thenReturn(true);
- switchUserToCreatorOrParentOf(config);
- final WifiConfiguration configCopy = new WifiConfiguration(config);
- int networkId = config.networkId;
- config.networkId = -1;
- when(mWifiNative.addNetwork()).thenReturn(networkId);
- when(mWifiNative.getNetworkVariable(networkId, WifiConfiguration.ssidVarName))
- .thenReturn(encodeConfigSSID(config));
- mWifiConfigManager.saveNetwork(config, configCopy.creatorUid);
+ when(mWifiConfigStore.areStoresPresent()).thenReturn(true);
+ setupStoreDataForRead(new ArrayList<WifiConfiguration>(),
+ new ArrayList<WifiConfiguration>(), new HashSet<String>());
- switchUser(originalUserId);
- }
-
- private String encodeConfigSSID(WifiConfiguration config) throws Exception {
- return new BigInteger(1, config.SSID.substring(1, config.SSID.length() - 1)
- .getBytes("UTF-8")).toString(16);
+ when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(), anyInt()))
+ .thenReturn(false);
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ when(mWifiPermissionsWrapper.getDevicePolicyManagerInternal())
+ .thenReturn(mDevicePolicyManagerInternal);
+ createWifiConfigManager();
+ mWifiConfigManager.setOnSavedNetworkUpdateListener(mWcmListener);
}
/**
- * Verifies that getConfiguredNetworksSize() returns the number of network configurations
- * visible to the current user.
+ * Called after each test
+ */
+ @After
+ public void cleanup() {
+ validateMockitoUsage();
+ }
+
+ /**
+ * Verifies that network retrieval via
+ * {@link WifiConfigManager#getConfiguredNetworks()} and
+ * {@link WifiConfigManager#getConfiguredNetworksWithPasswords()} works even if we have not
+ * yet loaded data from store.
*/
@Test
- public void testGetConfiguredNetworksSize() throws Exception {
- addNetworks();
- for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) {
- switchUser(entry.getKey());
- assertEquals(entry.getValue().size(), mWifiConfigManager.getConfiguredNetworksSize());
- }
- }
-
- private void verifyNetworkConfig(WifiConfiguration expectedConfig,
- WifiConfiguration actualConfig) {
- assertNotNull(actualConfig);
- assertEquals(expectedConfig.SSID, actualConfig.SSID);
- assertEquals(expectedConfig.FQDN, actualConfig.FQDN);
- assertEquals(expectedConfig.providerFriendlyName,
- actualConfig.providerFriendlyName);
- assertEquals(expectedConfig.configKey(), actualConfig.configKey(false));
- }
-
- private void verifyNetworkConfigs(Collection<WifiConfiguration> expectedConfigs,
- Collection<WifiConfiguration> actualConfigs) {
- assertEquals(expectedConfigs.size(), actualConfigs.size());
- for (WifiConfiguration expectedConfig : expectedConfigs) {
- WifiConfiguration actualConfig = null;
- // Find the network configuration to test (assume that |actualConfigs| contains them in
- // undefined order).
- for (final WifiConfiguration candidate : actualConfigs) {
- if (candidate.networkId == expectedConfig.networkId) {
- actualConfig = candidate;
- break;
- }
- }
- verifyNetworkConfig(expectedConfig, actualConfig);
- }
+ public void testGetConfiguredNetworksBeforeLoadFromStore() {
+ assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ assertTrue(mWifiConfigManager.getConfiguredNetworksWithPasswords().isEmpty());
}
/**
- * Verifies that getConfiguredNetworksSize() returns the network configurations visible to the
- * current user.
+ * Verifies that network addition via
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} fails if we have not
+ * yet loaded data from store.
*/
@Test
- public void testGetConfiguredNetworks() throws Exception {
- addNetworks();
- for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) {
- switchUser(entry.getKey());
- verifyNetworkConfigs(entry.getValue(), mWifiConfigManager.getSavedNetworks());
- }
+ public void testAddNetworkBeforeLoadFromStore() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ assertFalse(
+ mWifiConfigManager.addOrUpdateNetwork(openNetwork, TEST_CREATOR_UID).isSuccess());
}
/**
- * Verifies that getPrivilegedConfiguredNetworks() returns the network configurations visible to
- * the current user.
+ * Verifies the addition of a single network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
*/
@Test
- public void testGetPrivilegedConfiguredNetworks() throws Exception {
- addNetworks();
- for (Map.Entry<Integer, List<WifiConfiguration>> entry : VISIBLE_CONFIGS.entrySet()) {
- switchUser(entry.getKey());
- verifyNetworkConfigs(entry.getValue(),
- mWifiConfigManager.getPrivilegedSavedNetworks());
- }
+ 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);
+ // Ensure that the newly added network is disabled.
+ assertEquals(WifiConfiguration.Status.DISABLED, retrievedNetworks.get(0).status);
}
/**
- * Verifies that getWifiConfiguration(int netId) can be used to access network configurations
- * visible to the current user only.
+ * Verifies the modification of a single network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
*/
@Test
- public void testGetWifiConfigurationByNetworkId() throws Exception {
- addNetworks();
- for (int userId : USER_IDS) {
- switchUser(userId);
- for (WifiConfiguration expectedConfig: CONFIGS) {
- final WifiConfiguration actualConfig =
- mWifiConfigManager.getWifiConfiguration(expectedConfig.networkId);
- if (WifiConfigurationUtil.isVisibleToAnyProfile(expectedConfig,
- USER_PROFILES.get(userId))) {
- verifyNetworkConfig(expectedConfig, actualConfig);
- } else {
- assertNull(actualConfig);
- }
- }
- }
+ public void testUpdateSingleOpenNetwork() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
+
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+ verify(mWcmListener).onSavedNetworkAdded(openNetwork.networkId);
+ reset(mWcmListener);
+
+ // 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);
+ verify(mWcmListener).onSavedNetworkUpdated(openNetwork.networkId);
}
/**
- * Verifies that getWifiConfiguration(String key) can be used to access network configurations
- * visible to the current user only.
+ * Verifies the addition of a single ephemeral network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} and verifies that
+ * the {@link WifiConfigManager#getSavedNetworks()} does not return this network.
*/
@Test
- public void testGetWifiConfigurationByConfigKey() throws Exception {
- addNetworks();
- for (int userId : USER_IDS) {
- switchUser(userId);
- for (WifiConfiguration expectedConfig: CONFIGS) {
- final WifiConfiguration actualConfig =
- mWifiConfigManager.getWifiConfiguration(expectedConfig.configKey());
- if (WifiConfigurationUtil.isVisibleToAnyProfile(expectedConfig,
- USER_PROFILES.get(userId))) {
- verifyNetworkConfig(expectedConfig, actualConfig);
- } else {
- assertNull(actualConfig);
- }
- }
- }
+ public void testAddSingleEphemeralNetwork() throws Exception {
+ WifiConfiguration ephemeralNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ ephemeralNetwork.ephemeral = true;
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(ephemeralNetwork);
+
+ verifyAddEphemeralNetworkToWifiConfigManager(ephemeralNetwork);
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+
+ // Ensure that this is not returned in the saved network list.
+ assertTrue(mWifiConfigManager.getSavedNetworks().isEmpty());
+ verify(mWcmListener, never()).onSavedNetworkAdded(ephemeralNetwork.networkId);
}
/**
- * Verifies that enableAllNetworks() enables all temporarily disabled network configurations
- * visible to the current user.
+ * Verifies the addition of 2 networks (1 normal and 1 ephemeral) using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} and ensures that
+ * the ephemeral network configuration is not persisted in config store.
*/
@Test
- public void testEnableAllNetworks() throws Exception {
- addNetworks();
- for (int userId : USER_IDS) {
- switchUser(userId);
+ public void testAddMultipleNetworksAndEnsureEphemeralNetworkNotPersisted() {
+ WifiConfiguration ephemeralNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ ephemeralNetwork.ephemeral = true;
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
- for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
- final WifiConfiguration.NetworkSelectionStatus status =
- config.getNetworkSelectionStatus();
- status.setNetworkSelectionStatus(WifiConfiguration.NetworkSelectionStatus
- .NETWORK_SELECTION_TEMPORARY_DISABLED);
- status.setNetworkSelectionDisableReason(
- WifiConfiguration.NetworkSelectionStatus.DISABLED_AUTHENTICATION_FAILURE);
- status.setDisableTime(System.currentTimeMillis() - 60 * 60 * 1000);
- }
+ assertTrue(addNetworkToWifiConfigManager(ephemeralNetwork).isSuccess());
+ assertTrue(addNetworkToWifiConfigManager(openNetwork).isSuccess());
- mWifiConfigManager.enableAllNetworks();
-
- for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
- assertEquals(WifiConfigurationUtil.isVisibleToAnyProfile(config,
- USER_PROFILES.get(userId)),
- config.getNetworkSelectionStatus().isNetworkEnabled());
- }
- }
+ // The open network addition should trigger a store write.
+ Pair<List<WifiConfiguration>, List<WifiConfiguration>> networkListStoreData =
+ captureWriteNetworksListStoreData();
+ List<WifiConfiguration> networkList = new ArrayList<>();
+ networkList.addAll(networkListStoreData.first);
+ networkList.addAll(networkListStoreData.second);
+ assertFalse(isNetworkInConfigStoreData(ephemeralNetwork, networkList));
+ assertTrue(isNetworkInConfigStoreData(openNetwork, networkList));
}
/**
- * Verifies that selectNetwork() disables all network configurations visible to the current user
- * except the selected one.
+ * Verifies that the modification of a single open network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} with a UID which
+ * has no permission to modify the network fails.
*/
@Test
- public void testSelectNetwork() throws Exception {
- addNetworks();
+ public void testUpdateSingleOpenNetworkFailedDueToPermissionDenied() throws Exception {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
- for (int userId : USER_IDS) {
- switchUser(userId);
+ verifyAddNetworkToWifiConfigManager(openNetwork);
- for (WifiConfiguration config : mConfiguredNetworks.valuesForAllUsers()) {
- // Enable all network configurations.
- for (WifiConfiguration config2 : mConfiguredNetworks.valuesForAllUsers()) {
- config2.status = WifiConfiguration.Status.ENABLED;
- }
+ // Now change BSSID of the network.
+ assertAndSetNetworkBSSID(openNetwork, TEST_BSSID);
- // Try to select a network configuration.
- reset(mWifiNative);
- when(mWifiNative.selectNetwork(config.networkId)).thenReturn(true);
- final boolean success =
- mWifiConfigManager.selectNetwork(config, false, config.creatorUid);
- if (!WifiConfigurationUtil.isVisibleToAnyProfile(config,
- USER_PROFILES.get(userId))) {
- // If the network configuration is not visible to the current user, verify that
- // nothing changed.
- assertFalse(success);
- verify(mWifiNative, never()).selectNetwork(anyInt());
- verify(mWifiNative, never()).enableNetwork(anyInt());
- for (WifiConfiguration config2 : mConfiguredNetworks.valuesForAllUsers()) {
- 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.
- 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);
- }
- }
- }
- }
- }
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
+
+ // Update the same configuration and ensure that the operation failed.
+ NetworkUpdateResult result = updateNetworkToWifiConfigManager(openNetwork);
+ assertTrue(result.getNetworkId() == WifiConfiguration.INVALID_NETWORK_ID);
}
/**
- * Verifies that saveNetwork() correctly stores a network configuration in wpa_supplicant
- * variables and the networkHistory.txt file.
- * TODO: Test all variables. Currently, only the following variables are tested:
- * - In the wpa_supplicant: "ssid", "id_str"
- * - In networkHistory.txt: "CONFIG", "CREATOR_UID_KEY", "SHARED"
- */
- private void verifySaveNetwork(int network) throws Exception {
- // Switch to the correct user.
- switchUserToCreatorOrParentOf(CONFIGS.get(network));
-
- // Set up wpa_supplicant.
- when(mWifiNative.addNetwork()).thenReturn(0);
- when(mWifiNative.setNetworkVariable(eq(network), anyString(), anyString()))
- .thenReturn(true);
- when(mWifiNative.setNetworkExtra(eq(network), anyString(),
- (Map<String, String>) anyObject())).thenReturn(true);
- when(mWifiNative.getNetworkVariable(network, WifiConfiguration.ssidVarName))
- .thenReturn(encodeConfigSSID(CONFIGS.get(network)));
- when(mWifiNative.getNetworkVariable(network, WifiConfiguration.pmfVarName))
- .thenReturn("");
-
- // Store a network configuration.
- mWifiConfigManager.saveNetwork(CONFIGS.get(network), CONFIGS.get(network).creatorUid);
-
- // Verify that wpa_supplicant variables were written correctly for the network
- // 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(WifiConfigStore.ID_STRING_KEY_CONFIG_KEY, CONFIGS.get(network).configKey());
- metadata.put(WifiConfigStore.ID_STRING_KEY_CREATOR_UID,
- Integer.toString(CONFIGS.get(network).creatorUid));
- verify(mWifiNative).setNetworkExtra(network, WifiConfigStore.ID_STRING_VAR_NAME,
- metadata);
-
- // Verify that an attempt to read back the requirePMF variable was made.
- verify(mWifiNative).getNetworkVariable(network, WifiConfiguration.pmfVarName);
-
- // Verify that no wpa_supplicant variables were read or written for any other network
- // configurations.
- verify(mWifiNative, never()).setNetworkExtra(intThat(not(network)), anyString(),
- (Map<String, String>) anyObject());
- verify(mWifiNative, never()).setNetworkVariable(intThat(not(network)), anyString(),
- anyString());
- verify(mWifiNative, never()).getNetworkVariable(intThat(not(network)), anyString());
-
- // Parse networkHistory.txt.
- assertNotNull(mNetworkHistoryBytes);
- final DataInputStream stream =
- new DataInputStream(new ByteArrayInputStream(mNetworkHistoryBytes));
- List<String> keys = new ArrayList<>();
- List<String> values = new ArrayList<>();
- try {
- while (true) {
- final String[] tokens = stream.readUTF().split(":", 2);
- if (tokens.length == 2) {
- keys.add(tokens[0].trim());
- values.add(tokens[1].trim());
- }
- }
- } catch (EOFException e) {
- // Ignore. This is expected.
- }
-
- // Verify that a networkHistory.txt entry was written correctly for the network
- // configuration.
- assertTrue(keys.size() >= 3);
- assertEquals(WifiNetworkHistory.CONFIG_KEY, keys.get(0));
- assertEquals(CONFIGS.get(network).configKey(), values.get(0));
- final int creatorUidIndex = keys.indexOf(WifiNetworkHistory.CREATOR_UID_KEY);
- assertTrue(creatorUidIndex != -1);
- assertEquals(Integer.toString(CONFIGS.get(network).creatorUid),
- values.get(creatorUidIndex));
- final int sharedIndex = keys.indexOf(WifiNetworkHistory.SHARED_KEY);
- assertTrue(sharedIndex != -1);
- assertEquals(Boolean.toString(CONFIGS.get(network).shared), values.get(sharedIndex));
-
- // Verify that no networkHistory.txt entries were written for any other network
- // configurations.
- final int lastConfigIndex = keys.lastIndexOf(WifiNetworkHistory.CONFIG_KEY);
- assertEquals(0, lastConfigIndex);
- }
-
- /**
- * Verifies that saveNetwork() correctly stores a regular network configuration.
+ * Verifies that the modification of a single open network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} with the creator UID
+ * should always succeed.
*/
@Test
- public void testSaveNetworkRegular() throws Exception {
- verifySaveNetwork(0);
+ 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);
+
+ // 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 that saveNetwork() correctly stores a HotSpot 2.0 network configuration.
+ * Verifies the addition of a single PSK network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} and verifies that
+ * {@link WifiConfigManager#getSavedNetworks()} masks the password.
*/
@Test
- public void testSaveNetworkHotspot20() throws Exception {
- verifySaveNetwork(1);
+ 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 that saveNetwork() correctly stores a private network configuration.
+ * Verifies the addition of a single WEP network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} and verifies that
+ * {@link WifiConfigManager#getSavedNetworks()} masks the password.
*/
@Test
- public void testSaveNetworkPrivate() throws Exception {
- verifySaveNetwork(2);
+ 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 that loadConfiguredNetworks() correctly reads data from the wpa_supplicant, the
- * networkHistory.txt file and the MOManager, correlating the three sources based on the
- * configKey and the FQDN for HotSpot 2.0 networks.
- * TODO: Test all variables. Currently, only the following variables are tested:
- * - In the wpa_supplicant: "ssid", "id_str"
- * - In networkHistory.txt: "CONFIG", "CREATOR_UID_KEY", "SHARED"
+ * Verifies the modification of an IpConfiguration using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
*/
@Test
- public void testLoadConfiguredNetworks() throws Exception {
- // Set up list of network configurations returned by wpa_supplicant.
- final String header = "network id / ssid / bssid / flags";
- String networks = header;
- for (WifiConfiguration config : CONFIGS) {
- networks += "\n" + Integer.toString(config.networkId) + "\t" + config.SSID + "\tany";
- }
- when(mWifiNative.listNetworks(anyInt())).thenReturn(header);
- when(mWifiNative.listNetworks(-1)).thenReturn(networks);
+ public void testUpdateIpConfiguration() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(openNetwork);
- // Set up variables returned by wpa_supplicant for the individual network configurations.
- for (int i = 0; i < CONFIGS.size(); ++i) {
- when(mWifiNative.getNetworkVariable(i, WifiConfiguration.ssidVarName))
- .thenReturn(encodeConfigSSID(CONFIGS.get(i)));
- }
- // Legacy regular network configuration: No "id_str".
- when(mWifiNative.getNetworkExtra(0, WifiConfigStore.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))
- .thenReturn(null);
- when(mWifiNative.getNetworkVariable(1, WifiConfigStore.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,
- 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))
- .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,
- Integer.toString(CONFIGS.get(3).creatorUid));
- when(mWifiNative.getNetworkExtra(3, WifiConfigStore.ID_STRING_VAR_NAME))
- .thenReturn(metadata);
+ verifyAddNetworkToWifiConfigManager(openNetwork);
- // Set up networkHistory.txt file.
- final File file = File.createTempFile("networkHistory.txt", null);
- file.deleteOnExit();
+ // Now change BSSID of the network.
+ assertAndSetNetworkBSSID(openNetwork, TEST_BSSID);
- Field wifiNetworkHistoryConfigFile =
- WifiNetworkHistory.class.getDeclaredField("NETWORK_HISTORY_CONFIG_FILE");
- wifiNetworkHistoryConfigFile.setAccessible(true);
- wifiNetworkHistoryConfigFile.set(null, file.getAbsolutePath());
+ // Update the same configuration and ensure that the IP configuration change flags
+ // are not set.
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(openNetwork);
- final DataOutputStream stream = new DataOutputStream(new FileOutputStream(file));
- for (WifiConfiguration config : CONFIGS) {
- stream.writeUTF(WifiNetworkHistory.CONFIG_KEY + ": " + config.configKey() + '\n');
- stream.writeUTF(WifiNetworkHistory.CREATOR_UID_KEY + ": "
- + Integer.toString(config.creatorUid) + '\n');
- stream.writeUTF(WifiNetworkHistory.SHARED_KEY + ": "
- + Boolean.toString(config.shared) + '\n');
- }
- stream.close();
+ // Configure mock DevicePolicyManager to give Profile Owner permission so that we can modify
+ // proxy settings on a configuration
+ when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(),
+ eq(DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))).thenReturn(true);
- // Set up list of home service providers returned by MOManager.
- final List<HomeSP> homeSPs = new ArrayList<HomeSP>();
- for (WifiConfiguration config : CONFIGS) {
- if (config.FQDN != null) {
- homeSPs.add(new HomeSP(null, config.FQDN, new HashSet<Long>(),
- new HashSet<String>(),
- new HashSet<Long>(), new ArrayList<Long>(),
- config.providerFriendlyName, null,
- new Credential(0, 0, null, false, null, null),
- null, 0, null, null, null, 0));
- }
- }
- when(mMOManager.loadAllSPs()).thenReturn(homeSPs);
+ // Change the IpConfiguration now and ensure that the IP configuration flags are set now.
+ assertAndSetNetworkIpConfiguration(
+ openNetwork,
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy());
+ verifyUpdateNetworkToWifiConfigManagerWithIpChange(openNetwork);
- // Load network configurations.
- mWifiConfigManager.loadConfiguredNetworks();
-
- // Verify that network configurations were loaded and correlated correctly across the three
- // sources.
- verifyNetworkConfigs(CONFIGS, mConfiguredNetworks.valuesForAllUsers());
+ // Now verify that all the modifications have been effective.
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
}
/**
- * Verifies that loadConfiguredNetworks() correctly handles duplicates when reading network
- * configurations from the wpa_supplicant: The second configuration overwrites the first.
+ * Verifies the removal of a single network using
+ * {@link WifiConfigManager#removeNetwork(int)}
*/
@Test
- public void testLoadConfiguredNetworksEliminatesDuplicates() throws Exception {
- final WifiConfiguration config = new WifiConfiguration(CONFIGS.get(0));
- config.networkId = 1;
+ public void testRemoveSingleOpenNetwork() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
- // Set up list of network configurations returned by wpa_supplicant. The two configurations
- // are identical except for their network IDs.
- final String header = "network id / ssid / bssid / flags";
- final String networks =
- header + "\n0\t" + config.SSID + "\tany\n1\t" + config.SSID + "\tany";
- when(mWifiNative.listNetworks(anyInt())).thenReturn(header);
- when(mWifiNative.listNetworks(-1)).thenReturn(networks);
+ verifyAddNetworkToWifiConfigManager(openNetwork);
+ verify(mWcmListener).onSavedNetworkAdded(openNetwork.networkId);
+ reset(mWcmListener);
- // Set up variables returned by wpa_supplicant.
- 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,
- Integer.toString(config.creatorUid));
- when(mWifiNative.getNetworkExtra(anyInt(), eq(WifiConfigStore.ID_STRING_VAR_NAME)))
- .thenReturn(metadata);
+ // Ensure that configured network list is not empty.
+ assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty());
- // Load network configurations.
- mWifiConfigManager.loadConfiguredNetworks();
-
- // Verify that the second network configuration (network ID 1) overwrote the first (network
- // ID 0).
- verifyNetworkConfigs(Arrays.asList(config), mConfiguredNetworks.valuesForAllUsers());
+ verifyRemoveNetworkFromWifiConfigManager(openNetwork);
+ // Ensure that configured network list is empty now.
+ assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ verify(mWcmListener).onSavedNetworkRemoved(openNetwork.networkId);
}
/**
- * Verifies that handleUserSwitch() removes ephemeral network configurations, disables network
- * configurations that should no longer be visible and enables network configurations that
- * should become visible.
- */
- private void verifyHandleUserSwitch(int oldUserId, int newUserId,
- boolean makeOneConfigEphemeral) throws Exception {
- addNetworks();
- switchUser(oldUserId);
-
- reset(mWifiNative);
- final Field lastSelectedConfigurationField =
- WifiConfigManager.class.getDeclaredField("mLastSelectedConfiguration");
- lastSelectedConfigurationField.setAccessible(true);
- WifiConfiguration removedEphemeralConfig = null;
- final Set<WifiConfiguration> oldUserOnlyConfigs = new HashSet<>();
- final Set<WifiConfiguration> newUserOnlyConfigs = new HashSet<>();
- final Set<WifiConfiguration> neitherUserConfigs = new HashSet<>();
- final Collection<WifiConfiguration> oldConfigs = mConfiguredNetworks.valuesForAllUsers();
- int expectedNumberOfConfigs = oldConfigs.size();
- for (WifiConfiguration config : oldConfigs) {
- if (WifiConfigurationUtil.isVisibleToAnyProfile(config, USER_PROFILES.get(oldUserId))) {
- config.status = WifiConfiguration.Status.ENABLED;
- if (WifiConfigurationUtil.isVisibleToAnyProfile(config,
- USER_PROFILES.get(newUserId))) {
- if (makeOneConfigEphemeral && removedEphemeralConfig == null) {
- config.ephemeral = true;
- lastSelectedConfigurationField.set(mWifiConfigManager, config.configKey());
- removedEphemeralConfig = config;
- }
- } else {
- oldUserOnlyConfigs.add(config);
- }
- } else {
- config.status = WifiConfiguration.Status.DISABLED;
- if (WifiConfigurationUtil.isVisibleToAnyProfile(config,
- USER_PROFILES.get(newUserId))) {
- newUserOnlyConfigs.add(config);
- } else {
- neitherUserConfigs.add(config);
- }
- }
- }
-
- when(mWifiNative.disableNetwork(anyInt())).thenReturn(true);
- when(mWifiNative.removeNetwork(anyInt())).thenReturn(true);
-
- switchUser(newUserId);
- if (makeOneConfigEphemeral) {
- // Verify that the ephemeral network configuration was removed.
- assertNotNull(removedEphemeralConfig);
- assertNull(mConfiguredNetworks.getForAllUsers(removedEphemeralConfig.networkId));
- assertNull(lastSelectedConfigurationField.get(mWifiConfigManager));
- verify(mWifiNative).removeNetwork(removedEphemeralConfig.networkId);
- --expectedNumberOfConfigs;
- } else {
- assertNull(removedEphemeralConfig);
- }
-
- // Verify that the other network configurations were revealed/hidden and enabled/disabled as
- // appropriate.
- final Collection<WifiConfiguration> newConfigs = mConfiguredNetworks.valuesForAllUsers();
- assertEquals(expectedNumberOfConfigs, newConfigs.size());
- for (WifiConfiguration config : newConfigs) {
- if (oldUserOnlyConfigs.contains(config)) {
- verify(mWifiNative).disableNetwork(config.networkId);
- assertEquals(WifiConfiguration.Status.DISABLED, config.status);
- } else {
- verify(mWifiNative, never()).disableNetwork(config.networkId);
- if (neitherUserConfigs.contains(config)) {
- assertEquals(WifiConfiguration.Status.DISABLED, config.status);
- } else {
- // Only enabled in networkSelection.
- assertTrue(config.getNetworkSelectionStatus().isNetworkEnabled());
- }
-
- }
- }
- }
-
- /**
- * Verifies that handleUserSwitch() behaves correctly when the user switch removes an ephemeral
- * network configuration and reveals a private network configuration.
+ * Verifies the removal of an ephemeral network using
+ * {@link WifiConfigManager#removeNetwork(int)}
*/
@Test
- public void testHandleUserSwitchWithEphemeral() throws Exception {
- verifyHandleUserSwitch(USER_IDS[2], USER_IDS[0], true);
+ public void testRemoveSingleEphemeralNetwork() throws Exception {
+ WifiConfiguration ephemeralNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ ephemeralNetwork.ephemeral = true;
+
+ verifyAddEphemeralNetworkToWifiConfigManager(ephemeralNetwork);
+ // Ensure that configured network list is not empty.
+ assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ verify(mWcmListener, never()).onSavedNetworkAdded(ephemeralNetwork.networkId);
+
+ verifyRemoveEphemeralNetworkFromWifiConfigManager(ephemeralNetwork);
+ // Ensure that configured network list is empty now.
+ assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ verify(mWcmListener, never()).onSavedNetworkRemoved(ephemeralNetwork.networkId);
}
/**
- * Verifies that handleUserSwitch() behaves correctly when the user switch hides a private
- * network configuration.
+ * Verifies the removal of a Passpoint network using
+ * {@link WifiConfigManager#removeNetwork(int)}
*/
@Test
- public void testHandleUserSwitchWithoutEphemeral() throws Exception {
- verifyHandleUserSwitch(USER_IDS[0], USER_IDS[2], false);
- }
+ public void testRemoveSinglePasspointNetwork() throws Exception {
+ WifiConfiguration passpointNetwork = WifiConfigurationTestUtil.createPasspointNetwork();
- @Test
- public void testSaveLoadEapNetworks() {
- testSaveLoadSingleEapNetwork("eap network", new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT0}));
- testSaveLoadSingleEapNetwork("eap network", new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT1, FakeKeys.CA_CERT0}));
+ verifyAddPasspointNetworkToWifiConfigManager(passpointNetwork);
+ // Ensure that configured network list is not empty.
+ assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ verify(mWcmListener, never()).onSavedNetworkAdded(passpointNetwork.networkId);
- }
-
- private void testSaveLoadSingleEapNetwork(String ssid, EnterpriseConfig eapConfig) {
- final HashMap<String, String> networkVariables = new HashMap<String, String>();
- reset(mWifiNative);
- when(mWifiNative.addNetwork()).thenReturn(0);
- when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenAnswer(
- new AnswerWithArguments() {
- public boolean answer(int netId, String name, String value) {
- // Verify that no wpa_supplicant variables were written for any other
- // network configurations.
- assertEquals(netId, 0);
- networkVariables.put(name, value);
- return true;
- }
- });
- when(mWifiNative.getNetworkVariable(anyInt(), anyString())).then(
- new AnswerWithArguments() {
- public String answer(int netId, String name) {
- // Verify that no wpa_supplicant variables were read for any other
- // network configurations.
- assertEquals(netId, 0);
- return networkVariables.get(name);
- }
- });
- when(mWifiNative.setNetworkExtra(eq(0), anyString(), (Map<String, String>) anyObject()))
- .thenReturn(true);
-
- WifiConfiguration config = new WifiConfiguration();
- config.SSID = ssid;
- config.creatorUid = Process.WIFI_UID;
- config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
- config.enterpriseConfig = eapConfig.enterpriseConfig;
-
- // Store a network configuration.
- mWifiConfigManager.saveNetwork(config, Process.WIFI_UID);
-
- // Verify that wpa_supplicant variables were written correctly for the network
- // configuration.
- verify(mWifiNative).addNetwork();
- assertEquals(eapConfig.eap,
- unquote(networkVariables.get(WifiEnterpriseConfig.EAP_KEY)));
- assertEquals(eapConfig.phase2,
- unquote(networkVariables.get(WifiEnterpriseConfig.PHASE2_KEY)));
- assertEquals(eapConfig.identity,
- unquote(networkVariables.get(WifiEnterpriseConfig.IDENTITY_KEY)));
- assertEquals(eapConfig.password,
- unquote(networkVariables.get(WifiEnterpriseConfig.PASSWORD_KEY)));
- assertSavedCaCerts(eapConfig,
- unquote(networkVariables.get(WifiEnterpriseConfig.CA_CERT_KEY)));
-
- // Prepare the scan result.
- final String header = "network id / ssid / bssid / flags";
- String networks = header + "\n" + Integer.toString(0) + "\t" + ssid + "\tany";
- when(mWifiNative.listNetworks(anyInt())).thenReturn(header);
- when(mWifiNative.listNetworks(-1)).thenReturn(networks);
-
- // Load back the configuration.
- mWifiConfigManager.loadConfiguredNetworks();
- List<WifiConfiguration> configs = mWifiConfigManager.getSavedNetworks();
- assertEquals(1, configs.size());
- WifiConfiguration loadedConfig = configs.get(0);
- assertEquals(ssid, unquote(loadedConfig.SSID));
- BitSet keyMgmt = new BitSet();
- keyMgmt.set(KeyMgmt.WPA_EAP);
- assertEquals(keyMgmt, loadedConfig.allowedKeyManagement);
- assertEquals(eapConfig.enterpriseConfig.getEapMethod(),
- loadedConfig.enterpriseConfig.getEapMethod());
- assertEquals(eapConfig.enterpriseConfig.getPhase2Method(),
- loadedConfig.enterpriseConfig.getPhase2Method());
- assertEquals(eapConfig.enterpriseConfig.getIdentity(),
- loadedConfig.enterpriseConfig.getIdentity());
- assertEquals(eapConfig.enterpriseConfig.getPassword(),
- loadedConfig.enterpriseConfig.getPassword());
- asserCaCertsAliasesMatch(eapConfig.caCerts,
- loadedConfig.enterpriseConfig.getCaCertificateAliases());
- }
-
- private String unquote(String value) {
- if (value == null) {
- return null;
- }
- int length = value.length();
- if ((length > 1) && (value.charAt(0) == '"')
- && (value.charAt(length - 1) == '"')) {
- return value.substring(1, length - 1);
- } else {
- return value;
- }
- }
-
- private void asserCaCertsAliasesMatch(X509Certificate[] certs, String[] aliases) {
- assertEquals(certs.length, aliases.length);
- List<String> aliasList = new ArrayList<String>(Arrays.asList(aliases));
- try {
- for (int i = 0; i < certs.length; i++) {
- byte[] certPem = Credentials.convertToPem(certs[i]);
- boolean found = false;
- for (int j = 0; j < aliasList.size(); j++) {
- byte[] keystoreCert = mMockKeyStore.getKeyBlob(Process.WIFI_UID,
- Credentials.CA_CERTIFICATE + aliasList.get(j)).blob;
- if (Arrays.equals(keystoreCert, certPem)) {
- found = true;
- aliasList.remove(j);
- break;
- }
- }
- assertTrue(found);
- }
- } catch (CertificateEncodingException | IOException e) {
- fail("Cannot convert CA certificate to encoded form.");
- }
- }
-
- private void assertSavedCaCerts(EnterpriseConfig eapConfig, String caCertVariable) {
- ArrayList<String> aliases = new ArrayList<String>();
- if (TextUtils.isEmpty(caCertVariable)) {
- // Do nothing.
- } else if (caCertVariable.startsWith(WifiEnterpriseConfig.CA_CERT_PREFIX)) {
- aliases.add(caCertVariable.substring(WifiEnterpriseConfig.CA_CERT_PREFIX.length()));
- } else if (caCertVariable.startsWith(WifiEnterpriseConfig.KEYSTORES_URI)) {
- String[] encodedAliases = TextUtils.split(
- caCertVariable.substring(WifiEnterpriseConfig.KEYSTORES_URI.length()),
- WifiEnterpriseConfig.CA_CERT_ALIAS_DELIMITER);
- for (String encodedAlias : encodedAliases) {
- String alias = WifiEnterpriseConfig.decodeCaCertificateAlias(encodedAlias);
- assertTrue(alias.startsWith(Credentials.CA_CERTIFICATE));
- aliases.add(alias.substring(Credentials.CA_CERTIFICATE.length()));
- }
- } else {
- fail("Unrecognized ca_cert variable: " + caCertVariable);
- }
- asserCaCertsAliasesMatch(eapConfig.caCerts, aliases.toArray(new String[aliases.size()]));
- }
-
- private static class EnterpriseConfig {
- public String eap;
- public String phase2;
- public String identity;
- public String password;
- public X509Certificate[] caCerts;
- public WifiEnterpriseConfig enterpriseConfig;
-
- public EnterpriseConfig(int eapMethod) {
- enterpriseConfig = new WifiEnterpriseConfig();
- enterpriseConfig.setEapMethod(eapMethod);
- eap = Eap.strings[eapMethod];
- }
- public EnterpriseConfig setPhase2(int phase2Method) {
- enterpriseConfig.setPhase2Method(phase2Method);
- phase2 = "auth=" + Phase2.strings[phase2Method];
- return this;
- }
- public EnterpriseConfig setIdentity(String identity, String password) {
- enterpriseConfig.setIdentity(identity);
- enterpriseConfig.setPassword(password);
- this.identity = identity;
- this.password = password;
- return this;
- }
- public EnterpriseConfig setCaCerts(X509Certificate[] certs) {
- enterpriseConfig.setCaCertificates(certs);
- caCerts = certs;
- return this;
- }
+ verifyRemovePasspointNetworkFromWifiConfigManager(passpointNetwork);
+ // Ensure that configured network list is empty now.
+ assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ verify(mWcmListener, never()).onSavedNetworkRemoved(passpointNetwork.networkId);
}
/**
- * Generates an array of unique random numbers below the specified maxValue.
- * Values range from 0 to maxValue-1.
- */
- private static ArrayDeque<Integer> getUniqueRandomNumberValues(
- int seed,
- int maxValue,
- int numValues) {
- assertTrue(numValues <= maxValue);
- Random rand = new Random(WifiTestUtil.getTestMethod().hashCode() + seed);
- ArrayDeque<Integer> randomNumberList = new ArrayDeque<>();
- for (int i = 0; i < numValues; i++) {
- int num = rand.nextInt(maxValue);
- while (randomNumberList.contains(num)) {
- num = rand.nextInt(maxValue);
- }
- randomNumberList.push(num);
- }
- return randomNumberList;
- }
-
- /**
- * Verifies that the networks in pnoNetworkList is sorted in the same order as the
- * network in expectedNetworkIDOrder list.
- */
- private static void verifyPnoNetworkListOrder(
- ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoNetworkList,
- ArrayList<Integer> expectedNetworkIdOrder) throws Exception {
- int i = 0;
- for (WifiScanner.PnoSettings.PnoNetwork pnoNetwork : pnoNetworkList) {
- Log.i(TAG, "PNO Network List Index: " + i + ", networkID: " + pnoNetwork.networkId);
- assertEquals("Expected network ID: " + pnoNetwork.networkId,
- pnoNetwork.networkId, expectedNetworkIdOrder.get(i++).intValue());
- }
- }
-
- /**
- * Verifies the retrieveDisconnectedPnoNetworkList API. The test verifies that the list
- * returned from the API is sorted as expected.
+ * Verify that a Passpoint network that's added by an app with {@link #TEST_CREATOR_UID} can
+ * be removed by WiFi Service with {@link Process#WIFI_UID}.
+ *
+ * @throws Exception
*/
@Test
- public void testDisconnectedPnoNetworkListCreation() throws Exception {
- addNetworks();
+ public void testRemovePasspointNetworkAddedByOther() throws Exception {
+ WifiConfiguration passpointNetwork = WifiConfigurationTestUtil.createPasspointNetwork();
- Random rand = new Random(WifiTestUtil.getTestMethod().hashCode());
+ // Passpoint network is added using TEST_CREATOR_UID.
+ verifyAddPasspointNetworkToWifiConfigManager(passpointNetwork);
+ // Ensure that configured network list is not empty.
+ assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty());
- // First assign random |numAssociation| values and verify that the list is sorted
- // in descending order of |numAssociation| values. Keep NetworkSelectionStatus
- // values constant.
- for (int userId : USER_IDS) {
- switchUser(userId);
- TreeMap<Integer, Integer> numAssociationToNetworkIdMap =
- new TreeMap<>(Collections.reverseOrder());
- ArrayDeque<Integer> numAssociationValues =
- getUniqueRandomNumberValues(
- 1, 10000, mConfiguredNetworks.valuesForCurrentUser().size());
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- config.numAssociation = numAssociationValues.pop();
- config.priority = rand.nextInt(10000);
- config.getNetworkSelectionStatus().setNetworkSelectionStatus(
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
- numAssociationToNetworkIdMap.put(config.numAssociation, config.networkId);
- Log.i(TAG, "networkID: " + config.networkId + ", numAssociation: "
- + config.numAssociation);
- }
- ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoNetworkList =
- mWifiConfigManager.retrieveDisconnectedPnoNetworkList();
- verifyPnoNetworkListOrder(pnoNetworkList,
- new ArrayList(numAssociationToNetworkIdMap.values()));
- }
+ assertTrue(mWifiConfigManager.removeNetwork(passpointNetwork.networkId, Process.WIFI_UID));
- // Assign random |priority| values and verify that the list is sorted in descending order
- // of |priority| values. Keep numAssociation/NetworkSelectionStatus values constant.
- for (int userId : USER_IDS) {
- switchUser(userId);
- TreeMap<Integer, Integer> priorityToNetworkIdMap =
- new TreeMap<>(Collections.reverseOrder());
- ArrayDeque<Integer> priorityValues =
- getUniqueRandomNumberValues(
- 2, 10000, mConfiguredNetworks.valuesForCurrentUser().size());
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- config.numAssociation = 0;
- config.priority = priorityValues.pop();
- config.getNetworkSelectionStatus().setNetworkSelectionStatus(
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
- priorityToNetworkIdMap.put(config.priority, config.networkId);
- Log.i(TAG, "networkID: " + config.networkId + ", priority: " + config.priority);
- }
- ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoNetworkList =
- mWifiConfigManager.retrieveDisconnectedPnoNetworkList();
- verifyPnoNetworkListOrder(pnoNetworkList,
- new ArrayList(priorityToNetworkIdMap.values()));
- }
+ // Verify keys are not being removed.
+ verify(mWifiKeyStore, never()).removeKeys(any(WifiEnterpriseConfig.class));
+ verifyNetworkRemoveBroadcast(passpointNetwork);
+ // Ensure that the write was not invoked for Passpoint network remove.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
- // Now assign random |NetworkSelectionStatus| values and verify that the list is sorted in
- // ascending order of |NetworkSelectionStatus| values.
- for (int userId : USER_IDS) {
- switchUser(userId);
- TreeMap<Integer, Integer> networkSelectionStatusToNetworkIdMap = new TreeMap<>();
- ArrayDeque<Integer> networkSelectionStatusValues =
- getUniqueRandomNumberValues(
- 3,
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_STATUS_MAX,
- mConfiguredNetworks.valuesForCurrentUser().size());
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- config.numAssociation = rand.nextInt(10000);
- config.priority = rand.nextInt(10000);
- config.getNetworkSelectionStatus().setNetworkSelectionStatus(
- networkSelectionStatusValues.pop());
- // Permanently disabled networks should not be present in PNO scan request.
- if (!config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
- networkSelectionStatusToNetworkIdMap.put(
- config.getNetworkSelectionStatus().getNetworkSelectionStatus(),
- config.networkId);
- }
- Log.i(TAG, "networkID: " + config.networkId + ", NetworkSelectionStatus: "
- + config.getNetworkSelectionStatus().getNetworkSelectionStatus());
- }
- ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoNetworkList =
- mWifiConfigManager.retrieveDisconnectedPnoNetworkList();
- verifyPnoNetworkListOrder(pnoNetworkList,
- new ArrayList(networkSelectionStatusToNetworkIdMap.values()));
- }
+ }
+ /**
+ * Verifies the addition & update of multiple networks using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} and the
+ * removal of networks using
+ * {@link WifiConfigManager#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());
+
+ // Configure mock DevicePolicyManager to give Profile Owner permission so that we can modify
+ // proxy settings on a configuration
+ when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(),
+ eq(DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))).thenReturn(true);
+
+ 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 retrieveConnectedPnoNetworkList API. The test verifies that the list
- * returned from the API is sorted as expected.
+ * Verifies the update of network status using
+ * {@link WifiConfigManager#updateNetworkSelectionStatus(int, int)}.
*/
@Test
- public void testConnectedPnoNetworkListCreation() throws Exception {
- addNetworks();
+ public void testNetworkSelectionStatus() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
- Random rand = new Random(WifiTestUtil.getTestMethod().hashCode());
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
- // First assign |lastSeen| values and verify that the list is sorted
- // in descending order of |lastSeen| values. Keep NetworkSelectionStatus
- // values constant.
- for (int userId : USER_IDS) {
- switchUser(userId);
- TreeMap<Boolean, Integer> lastSeenToNetworkIdMap =
- new TreeMap<>(Collections.reverseOrder());
- ArrayDeque<Integer> lastSeenValues = getUniqueRandomNumberValues(1, 2, 2);
- if (mConfiguredNetworks.valuesForCurrentUser().size() > 2) continue;
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- config.numAssociation = rand.nextInt(10000);
- config.priority = rand.nextInt(10000);
- config.getNetworkSelectionStatus().setNetworkSelectionStatus(
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
- boolean lastSeenValue = (lastSeenValues.pop() == 1);
- config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(
- lastSeenValue);
- lastSeenToNetworkIdMap.put(lastSeenValue, config.networkId);
- Log.i(TAG, "networkID: " + config.networkId + ", lastSeen: " + lastSeenValue);
- }
- ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoNetworkList =
- mWifiConfigManager.retrieveConnectedPnoNetworkList();
- verifyPnoNetworkListOrder(pnoNetworkList,
- new ArrayList(lastSeenToNetworkIdMap.values()));
+ int networkId = result.getNetworkId();
+ // First set it to enabled.
+ verifyUpdateNetworkSelectionStatus(
+ networkId, 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 =
+ WifiConfigManager.NETWORK_SELECTION_DISABLE_THRESHOLD[assocRejectReason];
+ for (int i = 1; i <= assocRejectThreshold; i++) {
+ verifyUpdateNetworkSelectionStatus(result.getNetworkId(), assocRejectReason, i);
}
+ verify(mWcmListener).onSavedNetworkTemporarilyDisabled(networkId);
- // Assign random |numAssociation| values and verify that the list is sorted
- // in descending order of |numAssociation| values. Keep NetworkSelectionStatus/lastSeen
- // values constant.
- for (int userId : USER_IDS) {
- switchUser(userId);
- TreeMap<Integer, Integer> numAssociationToNetworkIdMap =
- new TreeMap<>(Collections.reverseOrder());
- ArrayDeque<Integer> numAssociationValues =
- getUniqueRandomNumberValues(
- 1, 10000, mConfiguredNetworks.valuesForCurrentUser().size());
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- config.numAssociation = numAssociationValues.pop();
- config.priority = rand.nextInt(10000);
- config.getNetworkSelectionStatus().setNetworkSelectionStatus(
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
- config.getNetworkSelectionStatus().setSeenInLastQualifiedNetworkSelection(true);
- numAssociationToNetworkIdMap.put(config.numAssociation, config.networkId);
- Log.i(TAG, "networkID: " + config.networkId + ", numAssociation: "
- + config.numAssociation);
- }
- ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoNetworkList =
- mWifiConfigManager.retrieveConnectedPnoNetworkList();
- verifyPnoNetworkListOrder(pnoNetworkList,
- new ArrayList(numAssociationToNetworkIdMap.values()));
- }
+ // Now set it to permanently disabled.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER, 0);
+ verify(mWcmListener).onSavedNetworkPermanentlyDisabled(networkId);
- // Now assign random |NetworkSelectionStatus| values and verify that the list is sorted in
- // ascending order of |NetworkSelectionStatus| values.
- for (int userId : USER_IDS) {
- switchUser(userId);
- TreeMap<Integer, Integer> networkSelectionStatusToNetworkIdMap = new TreeMap<>();
- ArrayDeque<Integer> networkSelectionStatusValues =
- getUniqueRandomNumberValues(
- 3,
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_STATUS_MAX,
- mConfiguredNetworks.valuesForCurrentUser().size());
- for (WifiConfiguration config : mConfiguredNetworks.valuesForCurrentUser()) {
- config.numAssociation = rand.nextInt(10000);
- config.priority = rand.nextInt(10000);
- config.getNetworkSelectionStatus().setNetworkSelectionStatus(
- networkSelectionStatusValues.pop());
- // Permanently disabled networks should not be present in PNO scan request.
- if (!config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) {
- networkSelectionStatusToNetworkIdMap.put(
- config.getNetworkSelectionStatus().getNetworkSelectionStatus(),
- config.networkId);
- }
- Log.i(TAG, "networkID: " + config.networkId + ", NetworkSelectionStatus: "
- + config.getNetworkSelectionStatus().getNetworkSelectionStatus());
- }
- ArrayList<WifiScanner.PnoSettings.PnoNetwork> pnoNetworkList =
- mWifiConfigManager.retrieveConnectedPnoNetworkList();
- verifyPnoNetworkListOrder(pnoNetworkList,
- new ArrayList(networkSelectionStatusToNetworkIdMap.values()));
- }
+ // Now set it back to enabled.
+ verifyUpdateNetworkSelectionStatus(
+ result.getNetworkId(), NetworkSelectionStatus.NETWORK_SELECTION_ENABLE, 0);
+ verify(mWcmListener, times(2)).onSavedNetworkEnabled(networkId);
}
/**
- * Verifies that hasEverConnected is false for a newly added network
+ * Verifies the update of network status using
+ * {@link WifiConfigManager#updateNetworkSelectionStatus(int, int)} and ensures that
+ * enabling a network clears out all the temporary disable counters.
*/
@Test
- public void testAddNetworkHasEverConnectedFalse() throws Exception {
- addNetwork(BASE_HAS_EVER_CONNECTED_CONFIG);
- WifiConfiguration checkConfig = mWifiConfigManager.getWifiConfiguration(
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- assertFalse("Adding a new network should not have hasEverConnected set to true.",
- checkConfig.getNetworkSelectionStatus().getHasEverConnected());
+ 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 that {@link WifiConfigManager#updateNetworkNotRecommended(int, boolean)} correctly
+ * updates the {@link NetworkSelectionStatus#mNotRecommended} bit.
+ */
+ @Test
+ public void testUpdateNetworkNotRecommended() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ // First retrieve the configuration and check this it does not have this bit set
+ WifiConfiguration retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.netId);
+
+ assertFalse(retrievedNetwork.getNetworkSelectionStatus().isNotRecommended());
+
+ // Update the network to be not recommended;
+ assertTrue(mWifiConfigManager.updateNetworkNotRecommended(
+ result.netId, true /* notRecommended*/));
+
+ retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.netId);
+
+ assertTrue(retrievedNetwork.getNetworkSelectionStatus().isNotRecommended());
+
+ // Update the network to no longer be not recommended
+ assertTrue(mWifiConfigManager.updateNetworkNotRecommended(
+ result.netId, false/* notRecommended*/));
+
+ retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(result.netId);
+
+ assertFalse(retrievedNetwork.getNetworkSelectionStatus().isNotRecommended());
+ }
+
+ /**
+ * Verifies the enabling of temporarily disabled network using
+ * {@link WifiConfigManager#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 =
+ WifiConfigManager.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 =
+ WifiConfigManager.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 WifiConfigManager#enableNetwork(int, boolean, int)} and
+ * {@link WifiConfigManager#disableNetwork(int, int)}.
+ */
+ @Test
+ public void testEnableDisableNetwork() throws Exception {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ assertTrue(mWifiConfigManager.enableNetwork(
+ result.getNetworkId(), false, TEST_CREATOR_UID));
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ NetworkSelectionStatus retrievedStatus = retrievedNetwork.getNetworkSelectionStatus();
+ assertTrue(retrievedStatus.isNetworkEnabled());
+ verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.ENABLED);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(eq(true));
+
+ // 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);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(eq(true));
+ }
+
+ /**
+ * Verifies the enabling of network using
+ * {@link WifiConfigManager#enableNetwork(int, boolean, 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(), false, TEST_CREATOR_UID));
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ NetworkSelectionStatus retrievedStatus = retrievedNetwork.getNetworkSelectionStatus();
+ assertTrue(retrievedStatus.isNetworkEnabled());
+ verifyUpdateNetworkStatus(retrievedNetwork, WifiConfiguration.Status.ENABLED);
+
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
+
+ // 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 WifiConfigManager#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);
+
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
+
+ // Now try to update the last connect UID with |TEST_UPDATE_UID|, it should fail and
+ // the lastConnectUid should remain the same.
+ 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 WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}.
+ */
+ @Test
+ public void testAddOrUpdateNetworkWithNullConfig() {
+ NetworkUpdateResult result = mWifiConfigManager.addOrUpdateNetwork(null, TEST_CREATOR_UID);
+ assertFalse(result.isSuccess());
+ }
+
+ /**
+ * Verifies that attempting to remove a network without any configs stored will return false.
+ * This tests the case where we have not loaded any configs, potentially due to a pending store
+ * read.
+ * This invokes {@link WifiConfigManager#removeNetwork(int)}.
+ */
+ @Test
+ public void testRemoveNetworkWithEmptyConfigStore() {
+ int networkId = new Random().nextInt();
+ assertFalse(mWifiConfigManager.removeNetwork(networkId, TEST_CREATOR_UID));
+ }
+
+ /**
+ * Verifies that any configuration removal attempt with an invalid networkID is gracefully
+ * handled.
+ * This invokes {@link WifiConfigManager#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, TEST_CREATOR_UID));
+ }
+
+ /**
+ * Verifies that any configuration update attempt with an invalid networkID is gracefully
+ * handled.
+ * This invokes {@link WifiConfigManager#enableNetwork(int, boolean, int)},
+ * {@link WifiConfigManager#disableNetwork(int, int)},
+ * {@link WifiConfigManager#updateNetworkSelectionStatus(int, int)} and
+ * {@link WifiConfigManager#checkAndUpdateLastConnectUid(int, int)}.
+ */
+ @Test
+ public void testChangeConfigurationWithInvalidNetworkId() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ assertFalse(mWifiConfigManager.enableNetwork(
+ result.getNetworkId() + 1, false, 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 WifiConfigManager#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 WifiConfigManager#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 WifiConfigManager#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.clear();
+ network.allowedProtocols.clear();
+ network.allowedKeyManagement.clear();
+ network.allowedPairwiseCiphers.clear();
+ network.allowedGroupCiphers.clear();
+ network.setIpConfiguration(null);
+ network.enterpriseConfig = null;
+
+ // Update the network.
+ NetworkUpdateResult result = updateNetworkToWifiConfigManager(network);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertFalse(result.isNewNetwork());
+
+ // Verify no changes to the original network configuration.
+ verifyNetworkUpdateBroadcast(originalNetwork);
+ verifyNetworkInConfigStoreData(originalNetwork);
+ assertFalse(result.hasIpChanged());
+ assertFalse(result.hasProxyChanged());
+
+ // 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 that the modification of a single network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} does not modify
+ * existing configuration if there is a failure.
+ */
+ @Test
+ public void testUpdateSingleNetworkFailureDoesNotModifyOriginal() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createEapNetwork();
+ network.enterpriseConfig =
+ WifiConfigurationTestUtil.createPEAPWifiEnterpriseConfigWithGTCPhase2();
+ verifyAddNetworkToWifiConfigManager(network);
+
+ // Save a copy of the original network for comparison.
+ WifiConfiguration originalNetwork = new WifiConfiguration(network);
+
+ // Now modify the network's EAP method.
+ network.enterpriseConfig =
+ WifiConfigurationTestUtil.createTLSWifiEnterpriseConfigWithNonePhase2();
+
+ // Fail this update because of cert installation failure.
+ when(mWifiKeyStore
+ .updateNetworkKeys(any(WifiConfiguration.class), any(WifiConfiguration.class)))
+ .thenReturn(false);
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(network, TEST_UPDATE_UID);
+ assertTrue(result.getNetworkId() == WifiConfiguration.INVALID_NETWORK_ID);
+
+ // 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 WifiConfigManager#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 WifiConfigManager#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 ScanDetail added for a network is cached correctly.
+ */
+ @Test
+ public void testUpdateScanDetailForNetwork() {
+ // First add the provided network.
+ WifiConfiguration testNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(testNetwork);
+
+ // Now create a dummy scan detail corresponding to the network.
+ ScanDetail scanDetail = createScanDetailForNetwork(testNetwork);
+ ScanResult scanResult = scanDetail.getScanResult();
+
+ mWifiConfigManager.updateScanDetailForNetwork(result.getNetworkId(), scanDetail);
+
+ // Now retrieve the scan detail cache and ensure that the new scan detail is in cache.
+ ScanDetailCache retrievedScanDetailCache =
+ mWifiConfigManager.getScanDetailCacheForNetwork(result.getNetworkId());
+ assertEquals(1, retrievedScanDetailCache.size());
+ ScanResult retrievedScanResult = retrievedScanDetailCache.get(scanResult.BSSID);
+
+ ScanTestUtil.assertScanResultEquals(scanResult, retrievedScanResult);
+ }
+
+ /**
+ * Verifies that scan detail cache is trimmed down when the size of the cache for a network
+ * exceeds {@link WifiConfigManager#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 <= WifiConfigManager.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(WifiConfigManager.SCAN_CACHE_ENTRIES_TRIM_SIZE + 1, scanDetailCache.size());
+ }
+
+ /**
+ * Verifies that hasEverConnected is false for a newly added network.
+ */
+ @Test
+ public void testAddNetworkHasEverConnectedFalse() {
+ verifyAddNetworkHasEverConnectedFalse(WifiConfigurationTestUtil.createOpenNetwork());
+ }
/**
* Verifies that hasEverConnected is false for a newly added network even when new config has
* mistakenly set HasEverConnected to true.
- */
- @Test
- public void testAddNetworkOverridesHasEverConnectedWhenTrueInNewConfig() throws Exception {
- WifiConfiguration newNetworkWithHasEverConnectedTrue =
- new WifiConfiguration(BASE_HAS_EVER_CONNECTED_CONFIG);
- newNetworkWithHasEverConnectedTrue.getNetworkSelectionStatus().setHasEverConnected(true);
- addNetwork(newNetworkWithHasEverConnectedTrue);
- // check if addNetwork clears the bit.
- WifiConfiguration checkConfig = mWifiConfigManager.getWifiConfiguration(
- newNetworkWithHasEverConnectedTrue.networkId);
- assertFalse("Adding a new network should not have hasEverConnected set to true.",
- checkConfig.getNetworkSelectionStatus().getHasEverConnected());
- }
-
-
- /**
- * Verify that setting HasEverConnected with a config update can be read back.
*/
@Test
- public void testUpdateConfigToHasEverConnectedTrue() throws Exception {
- addNetwork(BASE_HAS_EVER_CONNECTED_CONFIG);
-
- // Get the newly saved config and update HasEverConnected
- WifiConfiguration checkConfig = mWifiConfigManager.getWifiConfiguration(
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- assertFalse("Adding a new network should not have hasEverConnected set to true.",
- checkConfig.getNetworkSelectionStatus().getHasEverConnected());
- checkConfig.getNetworkSelectionStatus().setHasEverConnected(true);
- mWifiConfigManager.addOrUpdateNetwork(checkConfig, HAS_EVER_CONNECTED_USER);
-
- // verify that HasEverConnected was properly written and read back
- checkHasEverConnectedTrue(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
+ public void testAddNetworkOverridesHasEverConnectedWhenTrueInNewConfig() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ openNetwork.getNetworkSelectionStatus().setHasEverConnected(true);
+ verifyAddNetworkHasEverConnectedFalse(openNetwork);
}
-
/**
- * Verifies that hasEverConnected is cleared when a network config preSharedKey is updated.
+ * Verify that the |HasEverConnected| is set when
+ * {@link WifiConfigManager#updateNetworkAfterConnect(int)} is invoked.
*/
@Test
- public void testUpdatePreSharedKeyClearsHasEverConnected() throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
-
- testUpdateConfigToHasEverConnectedTrue();
-
- WifiConfiguration original = mWifiConfigManager.getWifiConfiguration(
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- WifiConfiguration updatePreSharedKeyConfig = new WifiConfiguration();
- updatePreSharedKeyConfig.networkId = BASE_HAS_EVER_CONNECTED_CONFIG.networkId;
- updatePreSharedKeyConfig.SSID = original.SSID;
- updatePreSharedKeyConfig.preSharedKey = "newpassword";
- switchUserToCreatorOrParentOf(original);
- mWifiConfigManager.addOrUpdateNetwork(updatePreSharedKeyConfig,
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- checkHasEverConnectedFalse(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- switchUser(originalUserId);
+ public void testUpdateConfigAfterConnectHasEverConnectedTrue() {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ verifyAddNetworkHasEverConnectedFalse(openNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(openNetwork.networkId);
}
/**
- * Verifies that hasEverConnected is cleared when a network config allowedKeyManagement is
+ * Verifies that hasEverConnected is cleared when a network config |preSharedKey| is updated.
+ */
+ @Test
+ public void testUpdatePreSharedKeyClearsHasEverConnected() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkHasEverConnectedFalse(pskNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
+
+ // Now update the same network with a different psk.
+ assertFalse(pskNetwork.preSharedKey.equals("newpassword"));
+ pskNetwork.preSharedKey = "newpassword";
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(pskNetwork);
+ }
+
+ /**
+ * Verifies that hasEverConnected is cleared when a network config |wepKeys| is updated.
+ */
+ @Test
+ public void testUpdateWepKeysClearsHasEverConnected() {
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ verifyAddNetworkHasEverConnectedFalse(wepNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(wepNetwork.networkId);
+
+ // Now update the same network with a different wep.
+ assertFalse(wepNetwork.wepKeys[0].equals("newpassword"));
+ wepNetwork.wepKeys[0] = "newpassword";
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(wepNetwork);
+ }
+
+ /**
+ * Verifies that hasEverConnected is cleared when a network config |wepTxKeyIndex| is updated.
+ */
+ @Test
+ public void testUpdateWepTxKeyClearsHasEverConnected() {
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ verifyAddNetworkHasEverConnectedFalse(wepNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(wepNetwork.networkId);
+
+ // Now update the same network with a different wep.
+ assertFalse(wepNetwork.wepTxKeyIndex == 3);
+ wepNetwork.wepTxKeyIndex = 3;
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(wepNetwork);
+ }
+
+ /**
+ * Verifies that hasEverConnected is cleared when a network config |allowedKeyManagement| is
* updated.
*/
@Test
- public void testUpdateAllowedKeyManagementChanged() throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
+ public void testUpdateAllowedKeyManagementClearsHasEverConnected() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkHasEverConnectedFalse(pskNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
- testUpdateConfigToHasEverConnectedTrue();
-
- WifiConfiguration updateAllowedKeyManagementConfig = new WifiConfiguration();
- updateAllowedKeyManagementConfig.networkId = BASE_HAS_EVER_CONNECTED_CONFIG.networkId;
- updateAllowedKeyManagementConfig.SSID = BASE_HAS_EVER_CONNECTED_CONFIG.SSID;
- updateAllowedKeyManagementConfig.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
-
- // Set up mock to allow the new value to be read back into the config
- String allowedKeyManagementString = makeString(
- updateAllowedKeyManagementConfig.allowedKeyManagement,
- WifiConfiguration.KeyMgmt.strings);
- when(mWifiNative.getNetworkVariable(BASE_HAS_EVER_CONNECTED_CONFIG.networkId,
- KeyMgmt.varName)).thenReturn(allowedKeyManagementString);
-
- switchUserToCreatorOrParentOf(BASE_HAS_EVER_CONNECTED_CONFIG);
- mWifiConfigManager.addOrUpdateNetwork(updateAllowedKeyManagementConfig,
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- checkHasEverConnectedFalse(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- switchUser(originalUserId);
+ assertFalse(pskNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ pskNetwork.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(pskNetwork);
}
/**
- * Verifies that hasEverConnected is cleared when a network config allowedProtocols is
+ * Verifies that hasEverConnected is cleared when a network config |allowedProtocol| is
* updated.
*/
@Test
- public void testUpdateAllowedProtocolsChanged() throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
+ public void testUpdateProtocolsClearsHasEverConnected() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkHasEverConnectedFalse(pskNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
- testUpdateConfigToHasEverConnectedTrue();
-
- WifiConfiguration updateAllowedProtocolsConfig = new WifiConfiguration();
- updateAllowedProtocolsConfig.networkId = BASE_HAS_EVER_CONNECTED_CONFIG.networkId;
- updateAllowedProtocolsConfig.SSID = BASE_HAS_EVER_CONNECTED_CONFIG.SSID;
- updateAllowedProtocolsConfig.allowedProtocols.set(
- WifiConfiguration.Protocol.RSN);
-
- // Set up mock to allow the new value to be read back into the config
- String allowedProtocolsString = makeString(
- updateAllowedProtocolsConfig.allowedProtocols,
- WifiConfiguration.Protocol.strings);
- when(mWifiNative.getNetworkVariable(BASE_HAS_EVER_CONNECTED_CONFIG.networkId,
- Protocol.varName)).thenReturn(allowedProtocolsString);
-
- switchUserToCreatorOrParentOf(BASE_HAS_EVER_CONNECTED_CONFIG);
- mWifiConfigManager.addOrUpdateNetwork(updateAllowedProtocolsConfig,
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- checkHasEverConnectedFalse(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- switchUser(originalUserId);
+ assertFalse(pskNetwork.allowedProtocols.get(WifiConfiguration.Protocol.OSEN));
+ pskNetwork.allowedProtocols.set(WifiConfiguration.Protocol.OSEN);
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(pskNetwork);
}
/**
- * Verifies that hasEverConnected is cleared when a network config allowedAuthAlgorithms is
+ * Verifies that hasEverConnected is cleared when a network config |allowedAuthAlgorithms| is
* updated.
*/
@Test
- public void testUpdateAllowedAuthAlgorithmsChanged() throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
+ public void testUpdateAllowedAuthAlgorithmsClearsHasEverConnected() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkHasEverConnectedFalse(pskNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
- testUpdateConfigToHasEverConnectedTrue();
-
- WifiConfiguration updateAllowedAuthAlgorithmsConfig = new WifiConfiguration();
- updateAllowedAuthAlgorithmsConfig.networkId = BASE_HAS_EVER_CONNECTED_CONFIG.networkId;
- updateAllowedAuthAlgorithmsConfig.SSID = BASE_HAS_EVER_CONNECTED_CONFIG.SSID;
- updateAllowedAuthAlgorithmsConfig.allowedAuthAlgorithms.set(
- WifiConfiguration.AuthAlgorithm.SHARED);
-
- // Set up mock to allow the new value to be read back into the config
- String allowedAuthAlgorithmsString = makeString(
- updateAllowedAuthAlgorithmsConfig.allowedAuthAlgorithms,
- WifiConfiguration.AuthAlgorithm.strings);
- when(mWifiNative.getNetworkVariable(BASE_HAS_EVER_CONNECTED_CONFIG.networkId,
- AuthAlgorithm.varName)).thenReturn(allowedAuthAlgorithmsString);
-
- switchUserToCreatorOrParentOf(BASE_HAS_EVER_CONNECTED_CONFIG);
- mWifiConfigManager.addOrUpdateNetwork(updateAllowedAuthAlgorithmsConfig,
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- checkHasEverConnectedFalse(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- switchUser(originalUserId);
+ assertFalse(pskNetwork.allowedAuthAlgorithms.get(WifiConfiguration.AuthAlgorithm.LEAP));
+ pskNetwork.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.LEAP);
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(pskNetwork);
}
/**
- * Verifies that hasEverConnected is cleared when a network config allowedPairwiseCiphers is
+ * Verifies that hasEverConnected is cleared when a network config |allowedPairwiseCiphers| is
* updated.
*/
@Test
- public void testUpdateAllowedPairwiseCiphersChanged() throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
+ public void testUpdateAllowedPairwiseCiphersClearsHasEverConnected() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkHasEverConnectedFalse(pskNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
- testUpdateConfigToHasEverConnectedTrue();
-
- WifiConfiguration updateAllowedPairwiseCiphersConfig = new WifiConfiguration();
- updateAllowedPairwiseCiphersConfig.networkId = BASE_HAS_EVER_CONNECTED_CONFIG.networkId;
- updateAllowedPairwiseCiphersConfig.SSID = BASE_HAS_EVER_CONNECTED_CONFIG.SSID;
- updateAllowedPairwiseCiphersConfig.allowedPairwiseCiphers.set(
- WifiConfiguration.PairwiseCipher.CCMP);
-
- // Set up mock to allow the new value to be read back into the config
- String allowedPairwiseCiphersString = makeString(
- updateAllowedPairwiseCiphersConfig.allowedPairwiseCiphers,
- WifiConfiguration.PairwiseCipher.strings);
- when(mWifiNative.getNetworkVariable(BASE_HAS_EVER_CONNECTED_CONFIG.networkId,
- PairwiseCipher.varName)).thenReturn(allowedPairwiseCiphersString);
-
- switchUserToCreatorOrParentOf(BASE_HAS_EVER_CONNECTED_CONFIG);
- mWifiConfigManager.addOrUpdateNetwork(updateAllowedPairwiseCiphersConfig,
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- checkHasEverConnectedFalse(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- switchUser(originalUserId);
+ assertFalse(pskNetwork.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.NONE));
+ pskNetwork.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.NONE);
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(pskNetwork);
}
/**
- * Verifies that hasEverConnected is cleared when a network config allowedGroupCiphers is
+ * Verifies that hasEverConnected is cleared when a network config |allowedGroup| is
* updated.
*/
@Test
- public void testUpdateAllowedGroupCiphersChanged() throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
+ public void testUpdateAllowedGroupCiphersClearsHasEverConnected() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkHasEverConnectedFalse(pskNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
- testUpdateConfigToHasEverConnectedTrue();
-
- WifiConfiguration updateAllowedGroupCiphersConfig = new WifiConfiguration();
- updateAllowedGroupCiphersConfig.networkId = BASE_HAS_EVER_CONNECTED_CONFIG.networkId;
- updateAllowedGroupCiphersConfig.SSID = BASE_HAS_EVER_CONNECTED_CONFIG.SSID;
- updateAllowedGroupCiphersConfig.allowedGroupCiphers.set(
- WifiConfiguration.GroupCipher.CCMP);
-
- // Set up mock to allow the new value to be read back into the config
- String allowedGroupCiphersString = makeString(
- updateAllowedGroupCiphersConfig.allowedGroupCiphers,
- WifiConfiguration.GroupCipher.strings);
- when(mWifiNative.getNetworkVariable(BASE_HAS_EVER_CONNECTED_CONFIG.networkId,
- GroupCipher.varName)).thenReturn(allowedGroupCiphersString);
-
- switchUserToCreatorOrParentOf(BASE_HAS_EVER_CONNECTED_CONFIG);
- mWifiConfigManager.addOrUpdateNetwork(updateAllowedGroupCiphersConfig,
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- checkHasEverConnectedFalse(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- switchUser(originalUserId);
+ assertTrue(pskNetwork.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.WEP104));
+ pskNetwork.allowedGroupCiphers.clear(WifiConfiguration.GroupCipher.WEP104);
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(pskNetwork);
}
/**
- * Verifies that hasEverConnected is cleared when a network config wepKeys is
+ * Verifies that hasEverConnected is cleared when a network config |hiddenSSID| is
* updated.
*/
@Test
- public void testUpdateWepKeysChanged() throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
+ public void testUpdateHiddenSSIDClearsHasEverConnected() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkHasEverConnectedFalse(pskNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
- testUpdateConfigToHasEverConnectedTrue();
-
- String tempKey = "hereisakey";
- WifiConfiguration updateWepKeysConfig = new WifiConfiguration();
- updateWepKeysConfig.networkId = BASE_HAS_EVER_CONNECTED_CONFIG.networkId;
- updateWepKeysConfig.SSID = BASE_HAS_EVER_CONNECTED_CONFIG.SSID;
- updateWepKeysConfig.wepKeys = new String[] {tempKey};
-
- // Set up mock to allow the new value to be read back into the config
- when(mWifiNative.getNetworkVariable(BASE_HAS_EVER_CONNECTED_CONFIG.networkId,
- WifiConfiguration.wepKeyVarNames[0])).thenReturn(tempKey);
-
- switchUserToCreatorOrParentOf(BASE_HAS_EVER_CONNECTED_CONFIG);
- mWifiConfigManager.addOrUpdateNetwork(updateWepKeysConfig,
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- checkHasEverConnectedFalse(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- switchUser(originalUserId);
+ assertFalse(pskNetwork.hiddenSSID);
+ pskNetwork.hiddenSSID = true;
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(pskNetwork);
}
/**
- * Verifies that hasEverConnected is cleared when a network config hiddenSSID is
+ * Verifies that hasEverConnected is not cleared when a network config |requirePMF| is
* updated.
*/
@Test
- public void testUpdateHiddenSSIDChanged() throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
+ public void testUpdateRequirePMFDoesNotClearHasEverConnected() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkHasEverConnectedFalse(pskNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(pskNetwork.networkId);
- testUpdateConfigToHasEverConnectedTrue();
+ assertFalse(pskNetwork.requirePMF);
+ pskNetwork.requirePMF = true;
- WifiConfiguration updateHiddenSSIDConfig = new WifiConfiguration();
- updateHiddenSSIDConfig.networkId = BASE_HAS_EVER_CONNECTED_CONFIG.networkId;
- updateHiddenSSIDConfig.SSID = BASE_HAS_EVER_CONNECTED_CONFIG.SSID;
- updateHiddenSSIDConfig.hiddenSSID = true;
-
- // Set up mock to allow the new value to be read back into the config
- when(mWifiNative.getNetworkVariable(BASE_HAS_EVER_CONNECTED_CONFIG.networkId,
- WifiConfiguration.hiddenSSIDVarName)).thenReturn("1");
-
- switchUserToCreatorOrParentOf(BASE_HAS_EVER_CONNECTED_CONFIG);
- mWifiConfigManager.addOrUpdateNetwork(updateHiddenSSIDConfig,
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- checkHasEverConnectedFalse(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- switchUser(originalUserId);
+ NetworkUpdateResult result =
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(pskNetwork);
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ assertTrue("Updating network non-credentials config should not clear hasEverConnected.",
+ retrievedNetwork.getNetworkSelectionStatus().getHasEverConnected());
+ assertFalse(result.hasCredentialChanged());
}
/**
- * Verifies that hasEverConnected is cleared when a network config pmfVarName is
+ * Verifies that hasEverConnected is cleared when a network config |enterpriseConfig| is
* updated.
*/
@Test
- public void testUpdateRequirePMFChanged() throws Exception {
- final int originalUserId = mWifiConfigManager.getCurrentUserId();
+ public void testUpdateEnterpriseConfigClearsHasEverConnected() {
+ WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork();
+ eapNetwork.enterpriseConfig =
+ WifiConfigurationTestUtil.createPEAPWifiEnterpriseConfigWithGTCPhase2();
+ verifyAddNetworkHasEverConnectedFalse(eapNetwork);
+ verifyUpdateNetworkAfterConnectHasEverConnectedTrue(eapNetwork.networkId);
- testUpdateConfigToHasEverConnectedTrue();
-
- WifiConfiguration updateRequirePMFConfig = new WifiConfiguration();
- updateRequirePMFConfig.networkId = BASE_HAS_EVER_CONNECTED_CONFIG.networkId;
- updateRequirePMFConfig.SSID = BASE_HAS_EVER_CONNECTED_CONFIG.SSID;
- 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
- // checkHasEverConnectedFalse below.
- //when(mWifiNative.getNetworkVariable(BASE_HAS_EVER_CONNECTED_CONFIG.networkId,
- // WifiConfiguration.pmfVarName)).thenReturn("2");
-
- switchUserToCreatorOrParentOf(BASE_HAS_EVER_CONNECTED_CONFIG);
- mWifiConfigManager.addOrUpdateNetwork(updateRequirePMFConfig,
- BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
-
- //checkHasEverConnectedFalse(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- checkHasEverConnectedTrue(BASE_HAS_EVER_CONNECTED_CONFIG.networkId);
- switchUser(originalUserId);
+ assertFalse(eapNetwork.enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.TLS);
+ eapNetwork.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(eapNetwork);
}
/**
- * Verify WifiEnterpriseConfig changes are detected in WifiConfigManager.
+ * Verifies that if the app sends back the masked passwords in an update, we ignore it.
*/
@Test
- public void testEnterpriseConfigAdded() {
- EnterpriseConfig eapConfig = new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT0});
+ public void testUpdateIgnoresMaskedPasswords() {
+ WifiConfiguration someRandomNetworkWithAllMaskedFields =
+ WifiConfigurationTestUtil.createEapNetwork();
+ someRandomNetworkWithAllMaskedFields.wepKeys = WifiConfigurationTestUtil.TEST_WEP_KEYS;
+ someRandomNetworkWithAllMaskedFields.preSharedKey = WifiConfigurationTestUtil.TEST_PSK;
+ someRandomNetworkWithAllMaskedFields.enterpriseConfig.setPassword(
+ WifiConfigurationTestUtil.TEST_EAP_PASSWORD);
- assertTrue(mWifiConfigManager.wasEnterpriseConfigChange(null, eapConfig.enterpriseConfig));
+ NetworkUpdateResult result =
+ verifyAddNetworkToWifiConfigManager(someRandomNetworkWithAllMaskedFields);
+
+ // All of these passwords must be masked in this retrieved network config.
+ WifiConfiguration retrievedNetworkWithMaskedPassword =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ assertPasswordsMaskedInWifiConfiguration(retrievedNetworkWithMaskedPassword);
+ // Ensure that the passwords are present internally.
+ WifiConfiguration retrievedNetworkWithPassword =
+ mWifiConfigManager.getConfiguredNetworkWithPassword(result.getNetworkId());
+ assertEquals(someRandomNetworkWithAllMaskedFields.preSharedKey,
+ retrievedNetworkWithPassword.preSharedKey);
+ assertEquals(someRandomNetworkWithAllMaskedFields.wepKeys,
+ retrievedNetworkWithPassword.wepKeys);
+ assertEquals(someRandomNetworkWithAllMaskedFields.enterpriseConfig.getPassword(),
+ retrievedNetworkWithPassword.enterpriseConfig.getPassword());
+
+ // Now update the same network config using the masked config.
+ verifyUpdateNetworkToWifiConfigManager(retrievedNetworkWithMaskedPassword);
+
+ // Retrieve the network config with password and ensure that they have not been overwritten
+ // with *.
+ retrievedNetworkWithPassword =
+ mWifiConfigManager.getConfiguredNetworkWithPassword(result.getNetworkId());
+ assertEquals(someRandomNetworkWithAllMaskedFields.preSharedKey,
+ retrievedNetworkWithPassword.preSharedKey);
+ assertEquals(someRandomNetworkWithAllMaskedFields.wepKeys,
+ retrievedNetworkWithPassword.wepKeys);
+ assertEquals(someRandomNetworkWithAllMaskedFields.enterpriseConfig.getPassword(),
+ retrievedNetworkWithPassword.enterpriseConfig.getPassword());
}
/**
- * Verify WifiEnterpriseConfig eap change is detected.
+ * Verifies the ordering of network list generated using
+ * {@link WifiConfigManager#retrievePnoNetworkList()}.
*/
@Test
- public void testEnterpriseConfigEapChangeDetected() {
- EnterpriseConfig eapConfig = new EnterpriseConfig(Eap.TTLS);
- EnterpriseConfig peapConfig = new EnterpriseConfig(Eap.PEAP);
+ public void testRetrievePnoList() {
+ // Create and add 3 networks.
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network3 = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
+ verifyAddNetworkToWifiConfigManager(network3);
- assertTrue(mWifiConfigManager.wasEnterpriseConfigChange(eapConfig.enterpriseConfig,
- peapConfig.enterpriseConfig));
+ // Enable all of them.
+ assertTrue(mWifiConfigManager.enableNetwork(network1.networkId, false, TEST_CREATOR_UID));
+ assertTrue(mWifiConfigManager.enableNetwork(network2.networkId, false, TEST_CREATOR_UID));
+ assertTrue(mWifiConfigManager.enableNetwork(network3.networkId, false, TEST_CREATOR_UID));
+
+ // Now set scan results in 2 of them to set the corresponding
+ // {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} field.
+ assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(
+ network1.networkId, createScanDetailForNetwork(network1).getScanResult(), 54));
+ assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(
+ network3.networkId, createScanDetailForNetwork(network3).getScanResult(), 54));
+
+ // Now increment |network3|'s association count. This should ensure that this network
+ // is preferred over |network1|.
+ assertTrue(mWifiConfigManager.updateNetworkAfterConnect(network3.networkId));
+
+ // Retrieve the Pno network list & verify the order of the networks returned.
+ List<WifiScanner.PnoSettings.PnoNetwork> pnoNetworks =
+ mWifiConfigManager.retrievePnoNetworkList();
+ assertEquals(3, pnoNetworks.size());
+ assertEquals(network3.SSID, pnoNetworks.get(0).ssid);
+ assertEquals(network1.SSID, pnoNetworks.get(1).ssid);
+ assertEquals(network2.SSID, pnoNetworks.get(2).ssid);
+
+ // Now permanently disable |network3|. This should remove network 3 from the list.
+ assertTrue(mWifiConfigManager.disableNetwork(network3.networkId, TEST_CREATOR_UID));
+
+ // Retrieve the Pno network list again & verify the order of the networks returned.
+ pnoNetworks = mWifiConfigManager.retrievePnoNetworkList();
+ assertEquals(2, pnoNetworks.size());
+ assertEquals(network1.SSID, pnoNetworks.get(0).ssid);
+ assertEquals(network2.SSID, pnoNetworks.get(1).ssid);
}
/**
- * Verify WifiEnterpriseConfig phase2 method change is detected.
+ * Verifies the linking of networks when they have the same default GW Mac address in
+ * {@link WifiConfigManager#getOrCreateScanDetailCacheForNetwork(WifiConfiguration)}.
*/
@Test
- public void testEnterpriseConfigPhase2ChangeDetected() {
- EnterpriseConfig eapConfig = new EnterpriseConfig(Eap.TTLS).setPhase2(Phase2.MSCHAPV2);
- EnterpriseConfig papConfig = new EnterpriseConfig(Eap.TTLS).setPhase2(Phase2.PAP);
+ public void testNetworkLinkUsingGwMacAddress() {
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network3 = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
+ verifyAddNetworkToWifiConfigManager(network3);
- assertTrue(mWifiConfigManager.wasEnterpriseConfigChange(eapConfig.enterpriseConfig,
- papConfig.enterpriseConfig));
+ // Set the same default GW mac address for all of the networks.
+ assertTrue(mWifiConfigManager.setNetworkDefaultGwMacAddress(
+ network1.networkId, TEST_DEFAULT_GW_MAC_ADDRESS));
+ assertTrue(mWifiConfigManager.setNetworkDefaultGwMacAddress(
+ network2.networkId, TEST_DEFAULT_GW_MAC_ADDRESS));
+ assertTrue(mWifiConfigManager.setNetworkDefaultGwMacAddress(
+ network3.networkId, TEST_DEFAULT_GW_MAC_ADDRESS));
+
+ // Now create dummy scan detail corresponding to the networks.
+ ScanDetail networkScanDetail1 = createScanDetailForNetwork(network1);
+ ScanDetail networkScanDetail2 = createScanDetailForNetwork(network2);
+ ScanDetail networkScanDetail3 = createScanDetailForNetwork(network3);
+
+ // Now save all these scan details corresponding to each of this network and expect
+ // all of these networks to be linked with each other.
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail1));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail3));
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworks();
+ for (WifiConfiguration network : retrievedNetworks) {
+ assertEquals(2, network.linkedConfigurations.size());
+ for (WifiConfiguration otherNetwork : retrievedNetworks) {
+ if (otherNetwork == network) {
+ continue;
+ }
+ assertNotNull(network.linkedConfigurations.get(otherNetwork.configKey()));
+ }
+ }
}
/**
- * Verify WifiEnterpriseConfig added Certificate is detected.
+ * Verifies the linking of networks when they have scan results with same first 16 ASCII of
+ * bssid in
+ * {@link WifiConfigManager#getOrCreateScanDetailCacheForNetwork(WifiConfiguration)}.
*/
@Test
- public void testCaCertificateAddedDetected() {
- EnterpriseConfig eapConfigNoCerts = new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password");
+ public void testNetworkLinkUsingBSSIDMatch() {
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network3 = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
+ verifyAddNetworkToWifiConfigManager(network3);
- EnterpriseConfig eapConfig1Cert = new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT0});
+ // Create scan results with bssid which is different in only the last char.
+ ScanDetail networkScanDetail1 = createScanDetailForNetwork(network1, "af:89:56:34:56:67");
+ ScanDetail networkScanDetail2 = createScanDetailForNetwork(network2, "af:89:56:34:56:68");
+ ScanDetail networkScanDetail3 = createScanDetailForNetwork(network3, "af:89:56:34:56:69");
- assertTrue(mWifiConfigManager.wasEnterpriseConfigChange(eapConfigNoCerts.enterpriseConfig,
- eapConfig1Cert.enterpriseConfig));
+ // Now save all these scan details corresponding to each of this network and expect
+ // all of these networks to be linked with each other.
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail1));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail3));
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworks();
+ for (WifiConfiguration network : retrievedNetworks) {
+ assertEquals(2, network.linkedConfigurations.size());
+ for (WifiConfiguration otherNetwork : retrievedNetworks) {
+ if (otherNetwork == network) {
+ continue;
+ }
+ assertNotNull(network.linkedConfigurations.get(otherNetwork.configKey()));
+ }
+ }
}
/**
- * Verify WifiEnterpriseConfig Certificate change is detected.
+ * Verifies the linking of networks does not happen for non WPA networks when they have scan
+ * results with same first 16 ASCII of bssid in
+ * {@link WifiConfigManager#getOrCreateScanDetailCacheForNetwork(WifiConfiguration)}.
*/
@Test
- public void testDifferentCaCertificateDetected() {
- EnterpriseConfig eapConfig = new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT0});
+ public void testNoNetworkLinkUsingBSSIDMatchForNonWpaNetworks() {
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createOpenNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
- EnterpriseConfig eapConfigNewCert = new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT1});
+ // Create scan results with bssid which is different in only the last char.
+ ScanDetail networkScanDetail1 = createScanDetailForNetwork(network1, "af:89:56:34:56:67");
+ ScanDetail networkScanDetail2 = createScanDetailForNetwork(network2, "af:89:56:34:56:68");
- assertTrue(mWifiConfigManager.wasEnterpriseConfigChange(eapConfig.enterpriseConfig,
- eapConfigNewCert.enterpriseConfig));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail1));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworks();
+ for (WifiConfiguration network : retrievedNetworks) {
+ assertNull(network.linkedConfigurations);
+ }
}
/**
- * Verify WifiEnterpriseConfig added Certificate changes are detected.
+ * Verifies the linking of networks does not happen for networks with more than
+ * {@link WifiConfigManager#LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES} scan
+ * results with same first 16 ASCII of bssid in
+ * {@link WifiConfigManager#getOrCreateScanDetailCacheForNetwork(WifiConfiguration)}.
*/
@Test
- public void testCaCertificateChangesDetected() {
- EnterpriseConfig eapConfig = new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT0});
+ public void testNoNetworkLinkUsingBSSIDMatchForNetworksWithHighScanDetailCacheSize() {
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
- EnterpriseConfig eapConfigAddedCert = new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT0, FakeKeys.CA_CERT1});
-
- assertTrue(mWifiConfigManager.wasEnterpriseConfigChange(eapConfig.enterpriseConfig,
- eapConfigAddedCert.enterpriseConfig));
- }
-
- /**
- * Verify that WifiEnterpriseConfig does not detect changes for identical configs.
- */
- @Test
- public void testWifiEnterpriseConfigNoChanges() {
- EnterpriseConfig eapConfig = new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT0, FakeKeys.CA_CERT1});
-
- // Just to be clear that check is not against the same object
- EnterpriseConfig eapConfigSame = new EnterpriseConfig(Eap.TTLS)
- .setPhase2(Phase2.MSCHAPV2)
- .setIdentity("username", "password")
- .setCaCerts(new X509Certificate[] {FakeKeys.CA_CERT0, FakeKeys.CA_CERT1});
-
- assertFalse(mWifiConfigManager.wasEnterpriseConfigChange(eapConfig.enterpriseConfig,
- eapConfigSame.enterpriseConfig));
- }
-
-
- private void checkHasEverConnectedTrue(int networkId) {
- WifiConfiguration checkConfig = mWifiConfigManager.getWifiConfiguration(networkId);
- assertTrue("hasEverConnected expected to be true.",
- checkConfig.getNetworkSelectionStatus().getHasEverConnected());
- }
-
- private void checkHasEverConnectedFalse(int networkId) {
- WifiConfiguration checkConfig = mWifiConfigManager.getWifiConfiguration(networkId);
- assertFalse("Updating credentials network config should clear hasEverConnected.",
- checkConfig.getNetworkSelectionStatus().getHasEverConnected());
- }
-
- /**
- * Helper function to translate from WifiConfiguration BitSet to String.
- */
- private static String makeString(BitSet set, String[] strings) {
- StringBuffer buf = new StringBuffer();
- int nextSetBit = -1;
-
- /* Make sure all set bits are in [0, strings.length) to avoid
- * going out of bounds on strings. (Shouldn't happen, but...) */
- set = set.get(0, strings.length);
-
- while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) {
- buf.append(strings[nextSetBit].replace('_', '-')).append(' ');
+ // Create 7 scan results with bssid which is different in only the last char.
+ String test_bssid_base = "af:89:56:34:56:6";
+ int scan_result_num = 0;
+ for (; scan_result_num < WifiConfigManager.LINK_CONFIGURATION_MAX_SCAN_CACHE_ENTRIES + 1;
+ scan_result_num++) {
+ ScanDetail networkScanDetail =
+ createScanDetailForNetwork(
+ network1, test_bssid_base + Integer.toString(scan_result_num));
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
}
- // remove trailing space
- if (set.cardinality() > 0) {
- buf.setLength(buf.length() - 1);
- }
+ // Now add 1 scan result to the other network with bssid which is different in only the
+ // last char.
+ ScanDetail networkScanDetail2 =
+ createScanDetailForNetwork(
+ network2, test_bssid_base + Integer.toString(scan_result_num++));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
- return buf.toString();
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworks();
+ for (WifiConfiguration network : retrievedNetworks) {
+ assertNull(network.linkedConfigurations);
+ }
}
+ /**
+ * Verifies the linking of networks when they have scan results with same first 16 ASCII of
+ * bssid in {@link WifiConfigManager#getOrCreateScanDetailCacheForNetwork(WifiConfiguration)}
+ * and then subsequently delinked when the networks have default gateway set which do not match.
+ */
+ @Test
+ public void testNetworkLinkUsingBSSIDMatchAndThenUnlinkDueToGwMacAddress() {
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
+
+ // Create scan results with bssid which is different in only the last char.
+ ScanDetail networkScanDetail1 = createScanDetailForNetwork(network1, "af:89:56:34:56:67");
+ ScanDetail networkScanDetail2 = createScanDetailForNetwork(network2, "af:89:56:34:56:68");
+
+ // Now save all these scan details corresponding to each of this network and expect
+ // all of these networks to be linked with each other.
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail1));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail2));
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworks();
+ for (WifiConfiguration network : retrievedNetworks) {
+ assertEquals(1, network.linkedConfigurations.size());
+ for (WifiConfiguration otherNetwork : retrievedNetworks) {
+ if (otherNetwork == network) {
+ continue;
+ }
+ assertNotNull(network.linkedConfigurations.get(otherNetwork.configKey()));
+ }
+ }
+
+ // Now Set different GW mac address for both the networks and ensure they're unlinked.
+ assertTrue(mWifiConfigManager.setNetworkDefaultGwMacAddress(
+ network1.networkId, "de:ad:fe:45:23:34"));
+ assertTrue(mWifiConfigManager.setNetworkDefaultGwMacAddress(
+ network2.networkId, "ad:de:fe:45:23:34"));
+
+ // Add some dummy scan results again to re-evaluate the linking of networks.
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(
+ createScanDetailForNetwork(network1, "af:89:56:34:45:67")));
+ assertNotNull(mWifiConfigManager.getSavedNetworkForScanDetailAndCache(
+ createScanDetailForNetwork(network1, "af:89:56:34:45:68")));
+
+ retrievedNetworks = mWifiConfigManager.getConfiguredNetworks();
+ for (WifiConfiguration network : retrievedNetworks) {
+ assertNull(network.linkedConfigurations);
+ }
+ }
+
+ /**
+ * Verifies the creation of channel list using
+ * {@link WifiConfigManager#fetchChannelSetForNetworkForPartialScan(int, long, int)}.
+ */
+ @Test
+ public void testFetchChannelSetForNetwork() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network);
+
+ // Create 5 scan results with different bssid's & frequencies.
+ String test_bssid_base = "af:89:56:34:56:6";
+ for (int i = 0; i < TEST_FREQ_LIST.length; i++) {
+ ScanDetail networkScanDetail =
+ createScanDetailForNetwork(
+ network, test_bssid_base + Integer.toString(i), 0, TEST_FREQ_LIST[i]);
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+
+ }
+ assertEquals(new HashSet<Integer>(Arrays.asList(TEST_FREQ_LIST)),
+ mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(network.networkId, 1,
+ TEST_FREQ_LIST[4]));
+ }
+
+ /**
+ * Verifies the creation of channel list using
+ * {@link WifiConfigManager#fetchChannelSetForNetworkForPartialScan(int, long, int)} and
+ * ensures that the frequenecy of the currently connected network is in the returned
+ * channel set.
+ */
+ @Test
+ public void testFetchChannelSetForNetworkIncludeCurrentNetwork() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network);
+
+ // Create 5 scan results with different bssid's & frequencies.
+ String test_bssid_base = "af:89:56:34:56:6";
+ for (int i = 0; i < TEST_FREQ_LIST.length; i++) {
+ ScanDetail networkScanDetail =
+ createScanDetailForNetwork(
+ network, test_bssid_base + Integer.toString(i), 0, TEST_FREQ_LIST[i]);
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+
+ }
+
+ // Currently connected network frequency 2427 is not in the TEST_FREQ_LIST
+ Set<Integer> freqs = mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(
+ network.networkId, 1, 2427);
+
+ assertEquals(true, freqs.contains(2427));
+ }
+
+ /**
+ * Verifies the creation of channel list using
+ * {@link WifiConfigManager#fetchChannelSetForNetworkForPartialScan(int, long, int)} and
+ * ensures that scan results which have a timestamp beyond the provided age are not used
+ * in the channel list.
+ */
+ @Test
+ public void testFetchChannelSetForNetworkIgnoresStaleScanResults() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network);
+
+ long wallClockBase = 0;
+ // Create 5 scan results with different bssid's & frequencies.
+ String test_bssid_base = "af:89:56:34:56:6";
+ for (int i = 0; i < TEST_FREQ_LIST.length; i++) {
+ // Increment the seen value in the scan results for each of them.
+ when(mClock.getWallClockMillis()).thenReturn(wallClockBase + i);
+ ScanDetail networkScanDetail =
+ createScanDetailForNetwork(
+ network, test_bssid_base + Integer.toString(i), 0, TEST_FREQ_LIST[i]);
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+
+ }
+ int ageInMillis = 4;
+ // Now fetch only scan results which are 4 millis stale. This should ignore the first
+ // scan result.
+ assertEquals(
+ new HashSet<>(Arrays.asList(
+ Arrays.copyOfRange(
+ TEST_FREQ_LIST,
+ TEST_FREQ_LIST.length - ageInMillis, TEST_FREQ_LIST.length))),
+ mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(
+ network.networkId, ageInMillis, TEST_FREQ_LIST[4]));
+ }
+
+ /**
+ * Verifies the creation of channel list using
+ * {@link WifiConfigManager#fetchChannelSetForNetworkForPartialScan(int, long, int)} and
+ * ensures that the list size does not exceed the max configured for the device.
+ */
+ @Test
+ public void testFetchChannelSetForNetworkIsLimitedToConfiguredSize() {
+ // Need to recreate the WifiConfigManager instance for this test to modify the config
+ // value which is read only in the constructor.
+ int maxListSize = 3;
+ mResources.setInteger(
+ R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels,
+ maxListSize);
+ createWifiConfigManager();
+
+ WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network);
+
+ // Create 5 scan results with different bssid's & frequencies.
+ String test_bssid_base = "af:89:56:34:56:6";
+ for (int i = 0; i < TEST_FREQ_LIST.length; i++) {
+ ScanDetail networkScanDetail =
+ createScanDetailForNetwork(
+ network, test_bssid_base + Integer.toString(i), 0, TEST_FREQ_LIST[i]);
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+
+ }
+ // Ensure that the fetched list size is limited.
+ assertEquals(maxListSize,
+ mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(
+ network.networkId, 1, TEST_FREQ_LIST[4]).size());
+ }
+
+ /**
+ * Verifies the creation of channel list using
+ * {@link WifiConfigManager#fetchChannelSetForNetworkForPartialScan(int, long, int)} and
+ * ensures that scan results from linked networks are used in the channel list.
+ */
+ @Test
+ public void testFetchChannelSetForNetworkIncludesLinkedNetworks() {
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
+
+ String test_bssid_base = "af:89:56:34:56:6";
+ int TEST_FREQ_LISTIdx = 0;
+ // Create 3 scan results with different bssid's & frequencies for network 1.
+ for (; TEST_FREQ_LISTIdx < TEST_FREQ_LIST.length / 2; TEST_FREQ_LISTIdx++) {
+ ScanDetail networkScanDetail =
+ createScanDetailForNetwork(
+ network1, test_bssid_base + Integer.toString(TEST_FREQ_LISTIdx), 0,
+ TEST_FREQ_LIST[TEST_FREQ_LISTIdx]);
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+
+ }
+ // Create 3 scan results with different bssid's & frequencies for network 2.
+ for (; TEST_FREQ_LISTIdx < TEST_FREQ_LIST.length; TEST_FREQ_LISTIdx++) {
+ ScanDetail networkScanDetail =
+ createScanDetailForNetwork(
+ network2, test_bssid_base + Integer.toString(TEST_FREQ_LISTIdx), 0,
+ TEST_FREQ_LIST[TEST_FREQ_LISTIdx]);
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ }
+
+ // Link the 2 configurations together using the GwMacAddress.
+ assertTrue(mWifiConfigManager.setNetworkDefaultGwMacAddress(
+ network1.networkId, TEST_DEFAULT_GW_MAC_ADDRESS));
+ assertTrue(mWifiConfigManager.setNetworkDefaultGwMacAddress(
+ network2.networkId, TEST_DEFAULT_GW_MAC_ADDRESS));
+
+ // The channel list fetched should include scan results from both the linked networks.
+ assertEquals(new HashSet<Integer>(Arrays.asList(TEST_FREQ_LIST)),
+ mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(network1.networkId, 1,
+ TEST_FREQ_LIST[0]));
+ assertEquals(new HashSet<Integer>(Arrays.asList(TEST_FREQ_LIST)),
+ mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(network2.networkId, 1,
+ TEST_FREQ_LIST[0]));
+ }
+
+ /**
+ * Verifies the creation of channel list using
+ * {@link WifiConfigManager#fetchChannelSetForNetworkForPartialScan(int, long, int)} and
+ * ensures that scan results from linked networks are used in the channel list and that the
+ * list size does not exceed the max configured for the device.
+ */
+ @Test
+ public void testFetchChannelSetForNetworkIncludesLinkedNetworksIsLimitedToConfiguredSize() {
+ // Need to recreate the WifiConfigManager instance for this test to modify the config
+ // value which is read only in the constructor.
+ int maxListSize = 3;
+ mResources.setInteger(
+ R.integer.config_wifi_framework_associated_partial_scan_max_num_active_channels,
+ maxListSize);
+
+ createWifiConfigManager();
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
+
+ String test_bssid_base = "af:89:56:34:56:6";
+ int TEST_FREQ_LISTIdx = 0;
+ // Create 3 scan results with different bssid's & frequencies for network 1.
+ for (; TEST_FREQ_LISTIdx < TEST_FREQ_LIST.length / 2; TEST_FREQ_LISTIdx++) {
+ ScanDetail networkScanDetail =
+ createScanDetailForNetwork(
+ network1, test_bssid_base + Integer.toString(TEST_FREQ_LISTIdx), 0,
+ TEST_FREQ_LIST[TEST_FREQ_LISTIdx]);
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+
+ }
+ // Create 3 scan results with different bssid's & frequencies for network 2.
+ for (; TEST_FREQ_LISTIdx < TEST_FREQ_LIST.length; TEST_FREQ_LISTIdx++) {
+ ScanDetail networkScanDetail =
+ createScanDetailForNetwork(
+ network2, test_bssid_base + Integer.toString(TEST_FREQ_LISTIdx), 0,
+ TEST_FREQ_LIST[TEST_FREQ_LISTIdx]);
+ assertNotNull(
+ mWifiConfigManager.getSavedNetworkForScanDetailAndCache(networkScanDetail));
+ }
+
+ // Link the 2 configurations together using the GwMacAddress.
+ assertTrue(mWifiConfigManager.setNetworkDefaultGwMacAddress(
+ network1.networkId, TEST_DEFAULT_GW_MAC_ADDRESS));
+ assertTrue(mWifiConfigManager.setNetworkDefaultGwMacAddress(
+ network2.networkId, TEST_DEFAULT_GW_MAC_ADDRESS));
+
+ // Ensure that the fetched list size is limited.
+ assertEquals(maxListSize,
+ mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(
+ network1.networkId, 1, TEST_FREQ_LIST[0]).size());
+ assertEquals(maxListSize,
+ mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(
+ network2.networkId, 1, TEST_FREQ_LIST[0]).size());
+ }
+
+ /**
+ * Verifies the foreground user switch using {@link WifiConfigManager#handleUserSwitch(int)}
+ * and ensures that any shared private networks networkId is not changed.
+ * Test scenario:
+ * 1. Load the shared networks from shared store and user 1 store.
+ * 2. Switch to user 2 and ensure that the shared network's Id is not changed.
+ */
+ @Test
+ public void testHandleUserSwitchDoesNotChangeSharedNetworksId() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ int appId = 674;
+
+ // Create 3 networks. 1 for user1, 1 for user2 and 1 shared.
+ final WifiConfiguration user1Network = WifiConfigurationTestUtil.createPskNetwork();
+ user1Network.shared = false;
+ user1Network.creatorUid = UserHandle.getUid(user1, appId);
+ final WifiConfiguration user2Network = WifiConfigurationTestUtil.createPskNetwork();
+ user2Network.shared = false;
+ user2Network.creatorUid = UserHandle.getUid(user2, appId);
+ final WifiConfiguration sharedNetwork1 = WifiConfigurationTestUtil.createPskNetwork();
+ final WifiConfiguration sharedNetwork2 = WifiConfigurationTestUtil.createPskNetwork();
+
+ // Set up the store data that is loaded initially.
+ List<WifiConfiguration> sharedNetworks = new ArrayList<WifiConfiguration>() {
+ {
+ add(sharedNetwork1);
+ add(sharedNetwork2);
+ }
+ };
+ List<WifiConfiguration> user1Networks = new ArrayList<WifiConfiguration>() {
+ {
+ add(user1Network);
+ }
+ };
+ setupStoreDataForRead(sharedNetworks, user1Networks, new HashSet<String>());
+ assertTrue(mWifiConfigManager.loadFromStore());
+ verify(mWifiConfigStore).read();
+
+ // Fetch the network ID's assigned to the shared networks initially.
+ int sharedNetwork1Id = WifiConfiguration.INVALID_NETWORK_ID;
+ int sharedNetwork2Id = WifiConfiguration.INVALID_NETWORK_ID;
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ for (WifiConfiguration network : retrievedNetworks) {
+ if (network.configKey().equals(sharedNetwork1.configKey())) {
+ sharedNetwork1Id = network.networkId;
+ } else if (network.configKey().equals(sharedNetwork2.configKey())) {
+ sharedNetwork2Id = network.networkId;
+ }
+ }
+ assertTrue(sharedNetwork1Id != WifiConfiguration.INVALID_NETWORK_ID);
+ assertTrue(sharedNetwork2Id != WifiConfiguration.INVALID_NETWORK_ID);
+
+ // Set up the user 2 store data that is loaded at user switch.
+ List<WifiConfiguration> user2Networks = new ArrayList<WifiConfiguration>() {
+ {
+ add(user2Network);
+ }
+ };
+ setupStoreDataForUserRead(user2Networks, new HashSet<String>());
+ // Now switch the user to user 2 and ensure that shared network's IDs have not changed.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
+ mWifiConfigManager.handleUserSwitch(user2);
+ verify(mWifiConfigStore).switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+
+ // Again fetch the network ID's assigned to the shared networks and ensure they have not
+ // changed.
+ int updatedSharedNetwork1Id = WifiConfiguration.INVALID_NETWORK_ID;
+ int updatedSharedNetwork2Id = WifiConfiguration.INVALID_NETWORK_ID;
+ retrievedNetworks = mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ for (WifiConfiguration network : retrievedNetworks) {
+ if (network.configKey().equals(sharedNetwork1.configKey())) {
+ updatedSharedNetwork1Id = network.networkId;
+ } else if (network.configKey().equals(sharedNetwork2.configKey())) {
+ updatedSharedNetwork2Id = network.networkId;
+ }
+ }
+ assertEquals(sharedNetwork1Id, updatedSharedNetwork1Id);
+ assertEquals(sharedNetwork2Id, updatedSharedNetwork2Id);
+ }
+
+ /**
+ * Verifies the foreground user switch using {@link WifiConfigManager#handleUserSwitch(int)}
+ * and ensures that any old user private networks are not visible anymore.
+ * Test scenario:
+ * 1. Load the shared networks from shared store and user 1 store.
+ * 2. Switch to user 2 and ensure that the user 1's private network has been removed.
+ */
+ @Test
+ public void testHandleUserSwitchRemovesOldUserPrivateNetworks() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ int appId = 674;
+
+ // Create 3 networks. 1 for user1, 1 for user2 and 1 shared.
+ final WifiConfiguration user1Network = WifiConfigurationTestUtil.createPskNetwork();
+ user1Network.shared = false;
+ user1Network.creatorUid = UserHandle.getUid(user1, appId);
+ final WifiConfiguration user2Network = WifiConfigurationTestUtil.createPskNetwork();
+ user2Network.shared = false;
+ user2Network.creatorUid = UserHandle.getUid(user2, appId);
+ final WifiConfiguration sharedNetwork = WifiConfigurationTestUtil.createPskNetwork();
+
+ // Set up the store data that is loaded initially.
+ List<WifiConfiguration> sharedNetworks = new ArrayList<WifiConfiguration>() {
+ {
+ add(sharedNetwork);
+ }
+ };
+ List<WifiConfiguration> user1Networks = new ArrayList<WifiConfiguration>() {
+ {
+ add(user1Network);
+ }
+ };
+ setupStoreDataForRead(sharedNetworks, user1Networks, new HashSet<String>());
+ assertTrue(mWifiConfigManager.loadFromStore());
+ verify(mWifiConfigStore).read();
+
+ // Fetch the network ID assigned to the user 1 network initially.
+ int user1NetworkId = WifiConfiguration.INVALID_NETWORK_ID;
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ for (WifiConfiguration network : retrievedNetworks) {
+ if (network.configKey().equals(user1Network.configKey())) {
+ user1NetworkId = network.networkId;
+ }
+ }
+
+ // Set up the user 2 store data that is loaded at user switch.
+ List<WifiConfiguration> user2Networks = new ArrayList<WifiConfiguration>() {
+ {
+ add(user2Network);
+ }
+ };
+ setupStoreDataForUserRead(user2Networks, new HashSet<String>());
+ // Now switch the user to user 2 and ensure that user 1's private network has been removed.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
+ Set<Integer> removedNetworks = mWifiConfigManager.handleUserSwitch(user2);
+ verify(mWifiConfigStore).switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ assertTrue((removedNetworks.size() == 1) && (removedNetworks.contains(user1NetworkId)));
+
+ // Set the expected networks to be |sharedNetwork| and |user2Network|.
+ List<WifiConfiguration> expectedNetworks = new ArrayList<WifiConfiguration>() {
+ {
+ add(sharedNetwork);
+ add(user2Network);
+ }
+ };
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ expectedNetworks, mWifiConfigManager.getConfiguredNetworksWithPasswords());
+
+ // Send another user switch indication with the same user 2. This should be ignored and
+ // hence should not remove any new networks.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
+ removedNetworks = mWifiConfigManager.handleUserSwitch(user2);
+ assertTrue(removedNetworks.isEmpty());
+ }
+
+ /**
+ * Verifies the foreground user switch using {@link WifiConfigManager#handleUserSwitch(int)}
+ * and ensures that user switch from a user with no private networks is handled.
+ * Test scenario:
+ * 1. Load the shared networks from shared store and emptu user 1 store.
+ * 2. Switch to user 2 and ensure that no private networks were removed.
+ */
+ @Test
+ public void testHandleUserSwitchWithNoOldUserPrivateNetworks() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ int appId = 674;
+
+ // Create 2 networks. 1 for user2 and 1 shared.
+ final WifiConfiguration user2Network = WifiConfigurationTestUtil.createPskNetwork();
+ user2Network.shared = false;
+ user2Network.creatorUid = UserHandle.getUid(user2, appId);
+ final WifiConfiguration sharedNetwork = WifiConfigurationTestUtil.createPskNetwork();
+
+ // Set up the store data that is loaded initially.
+ List<WifiConfiguration> sharedNetworks = new ArrayList<WifiConfiguration>() {
+ {
+ add(sharedNetwork);
+ }
+ };
+ setupStoreDataForRead(sharedNetworks, new ArrayList<WifiConfiguration>(),
+ new HashSet<String>());
+ assertTrue(mWifiConfigManager.loadFromStore());
+ verify(mWifiConfigStore).read();
+
+ // Set up the user 2 store data that is loaded at user switch.
+ List<WifiConfiguration> user2Networks = new ArrayList<WifiConfiguration>() {
+ {
+ add(user2Network);
+ }
+ };
+ setupStoreDataForUserRead(user2Networks, new HashSet<String>());
+ // Now switch the user to user 2 and ensure that no private network has been removed.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
+ Set<Integer> removedNetworks = mWifiConfigManager.handleUserSwitch(user2);
+ verify(mWifiConfigStore).switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ assertTrue(removedNetworks.isEmpty());
+ }
+
+ /**
+ * Verifies the foreground user switch using {@link WifiConfigManager#handleUserSwitch(int)}
+ * and ensures that any non current user private networks are moved to shared store file.
+ * This test simulates the following test case:
+ * 1. Loads the shared networks from shared store at bootup.
+ * 2. Load the private networks from user store on user 1 unlock.
+ * 3. Switch to user 2 and ensure that the user 2's private network has been moved to user 2's
+ * private store file.
+ */
+ @Test
+ public void testHandleUserSwitchPushesOtherPrivateNetworksToSharedStore() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ int appId = 674;
+
+ // Create 3 networks. 1 for user1, 1 for user2 and 1 shared.
+ final WifiConfiguration user1Network = WifiConfigurationTestUtil.createPskNetwork();
+ user1Network.shared = false;
+ user1Network.creatorUid = UserHandle.getUid(user1, appId);
+ final WifiConfiguration user2Network = WifiConfigurationTestUtil.createPskNetwork();
+ user2Network.shared = false;
+ user2Network.creatorUid = UserHandle.getUid(user2, appId);
+ final WifiConfiguration sharedNetwork = WifiConfigurationTestUtil.createPskNetwork();
+
+ // Set up the shared store data that is loaded at bootup. User 2's private network
+ // is still in shared store because they have not yet logged-in after upgrade.
+ List<WifiConfiguration> sharedNetworks = new ArrayList<WifiConfiguration>() {
+ {
+ add(sharedNetwork);
+ add(user2Network);
+ }
+ };
+ setupStoreDataForRead(sharedNetworks, new ArrayList<WifiConfiguration>(),
+ new HashSet<String>());
+ assertTrue(mWifiConfigManager.loadFromStore());
+ verify(mWifiConfigStore).read();
+
+ // Set up the user store data that is loaded at user unlock.
+ List<WifiConfiguration> userNetworks = new ArrayList<WifiConfiguration>() {
+ {
+ add(user1Network);
+ }
+ };
+ setupStoreDataForUserRead(userNetworks, new HashSet<String>());
+ mWifiConfigManager.handleUserUnlock(user1);
+ verify(mWifiConfigStore).switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ // Capture the written data for the user 1 and ensure that it corresponds to what was
+ // setup.
+ Pair<List<WifiConfiguration>, List<WifiConfiguration>> writtenNetworkList =
+ captureWriteNetworksListStoreData();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ sharedNetworks, writtenNetworkList.first);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ userNetworks, writtenNetworkList.second);
+
+ // Now switch the user to user2 and ensure that user 2's private network has been moved to
+ // the user store.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
+ mWifiConfigManager.handleUserSwitch(user2);
+ // Set the expected network list before comparing. user1Network should be in shared data.
+ // Note: In the real world, user1Network will no longer be visible now because it should
+ // already be in user1's private store file. But, we're purposefully exposing it
+ // via |loadStoreData| to test if other user's private networks are pushed to shared store.
+ List<WifiConfiguration> expectedSharedNetworks = new ArrayList<WifiConfiguration>() {
+ {
+ add(sharedNetwork);
+ add(user1Network);
+ }
+ };
+ List<WifiConfiguration> expectedUserNetworks = new ArrayList<WifiConfiguration>() {
+ {
+ add(user2Network);
+ }
+ };
+ // Capture the first written data triggered for saving the old user's network
+ // configurations.
+ writtenNetworkList = captureWriteNetworksListStoreData();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ sharedNetworks, writtenNetworkList.first);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ userNetworks, writtenNetworkList.second);
+
+ // Now capture the next written data triggered after the switch and ensure that user 2's
+ // network is now in user store data.
+ writtenNetworkList = captureWriteNetworksListStoreData();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ expectedSharedNetworks, writtenNetworkList.first);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ expectedUserNetworks, writtenNetworkList.second);
+ }
+
+ /**
+ * Verify that unlocking an user that owns a legacy Passpoint configuration (which is stored
+ * temporarily in the share store) will migrate it to PasspointManager and removed from
+ * the list of configured networks.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testHandleUserUnlockRemovePasspointConfigFromSharedConfig() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+ int appId = 674;
+
+ final WifiConfiguration passpointConfig =
+ WifiConfigurationTestUtil.createPasspointNetwork();
+ passpointConfig.creatorUid = UserHandle.getUid(user1, appId);
+ passpointConfig.isLegacyPasspointConfig = true;
+
+ // Set up the shared store data to contain one legacy Passpoint configuration.
+ List<WifiConfiguration> sharedNetworks = new ArrayList<WifiConfiguration>() {
+ {
+ add(passpointConfig);
+ }
+ };
+ setupStoreDataForRead(sharedNetworks, new ArrayList<WifiConfiguration>(),
+ new HashSet<String>());
+ assertTrue(mWifiConfigManager.loadFromStore());
+ verify(mWifiConfigStore).read();
+ assertEquals(1, mWifiConfigManager.getConfiguredNetworks().size());
+
+ // Unlock the owner of the legacy Passpoint configuration, verify it is removed from
+ // the configured networks (migrated to PasspointManager).
+ setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+ mWifiConfigManager.handleUserUnlock(user1);
+ verify(mWifiConfigStore).switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ Pair<List<WifiConfiguration>, List<WifiConfiguration>> writtenNetworkList =
+ captureWriteNetworksListStoreData();
+ assertTrue(writtenNetworkList.first.isEmpty());
+ assertTrue(writtenNetworkList.second.isEmpty());
+ assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ }
+
+ /**
+ * Verifies the foreground user switch using {@link WifiConfigManager#handleUserSwitch(int)}
+ * and {@link WifiConfigManager#handleUserUnlock(int)} and ensures that the new store is
+ * read immediately if the user is unlocked during the switch.
+ */
+ @Test
+ public void testHandleUserSwitchWhenUnlocked() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ // Set up the internal data first.
+ assertTrue(mWifiConfigManager.loadFromStore());
+
+ setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+ // user2 is unlocked and switched to foreground.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
+ mWifiConfigManager.handleUserSwitch(user2);
+ // Ensure that the read was invoked.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore)
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ }
+
+ /**
+ * Verifies the foreground user switch using {@link WifiConfigManager#handleUserSwitch(int)}
+ * and {@link WifiConfigManager#handleUserUnlock(int)} and ensures that the new store is not
+ * read until the user is unlocked.
+ */
+ public void testHandleUserSwitchWhenLocked() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ // Set up the internal data first.
+ assertTrue(mWifiConfigManager.loadFromStore());
+
+ // user2 is locked and switched to foreground.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(false);
+ mWifiConfigManager.handleUserSwitch(user2);
+
+ // Ensure that the read was not invoked.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+
+ // Now try unlocking some other user (user1), this should be ignored.
+ mWifiConfigManager.handleUserUnlock(user1);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+
+ setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+ // Unlock the user2 and ensure that we read the data now.
+ mWifiConfigManager.handleUserUnlock(user2);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore)
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ }
+
+ /**
+ * Verifies that the foreground user stop using {@link WifiConfigManager#handleUserStop(int)}
+ * and ensures that the store is written only when the foreground user is stopped.
+ */
+ @Test
+ public void testHandleUserStop() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ // Try stopping background user2 first, this should not do anything.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(false);
+ mWifiConfigManager.handleUserStop(user2);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+
+ // Now try stopping the foreground user1, this should trigger a write to store.
+ mWifiConfigManager.handleUserStop(user1);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean());
+ }
+
+ /**
+ * Verifies the foreground user unlock via {@link WifiConfigManager#handleUserUnlock(int)}
+ * results in a store read after bootup.
+ */
+ @Test
+ public void testHandleUserUnlockAfterBootup() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+
+ // Set up the internal data first.
+ assertTrue(mWifiConfigManager.loadFromStore());
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore).read();
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+
+ setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+ // Unlock the user1 (default user) for the first time and ensure that we read the data.
+ mWifiConfigManager.handleUserUnlock(user1);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read();
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore)
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean());
+ }
+
+ /**
+ * Verifies that the store read after bootup received after
+ * foreground user unlock via {@link WifiConfigManager#handleUserUnlock(int)}
+ * results in a user store read.
+ */
+ @Test
+ public void testHandleBootupAfterUserUnlock() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+
+ // Unlock the user1 (default user) for the first time and ensure that we don't read the
+ // data.
+ mWifiConfigManager.handleUserUnlock(user1);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).read();
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+
+ setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+ // Read from store now.
+ assertTrue(mWifiConfigManager.loadFromStore());
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore)
+ .setUserStore(any(WifiConfigStore.StoreFile.class));
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore).read();
+ }
+
+ /**
+ * Verifies the foreground user unlock via {@link WifiConfigManager#handleUserUnlock(int)} does
+ * not always result in a store read unless the user had switched or just booted up.
+ */
+ @Test
+ public void testHandleUserUnlockWithoutSwitchOrBootup() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ // Set up the internal data first.
+ assertTrue(mWifiConfigManager.loadFromStore());
+
+ setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+ // user2 is unlocked and switched to foreground.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
+ mWifiConfigManager.handleUserSwitch(user2);
+ // Ensure that the read was invoked.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore)
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+
+ // Unlock the user2 again and ensure that we don't read the data now.
+ mWifiConfigManager.handleUserUnlock(user2);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ }
+
+ /**
+ * Verifies the foreground user unlock via {@link WifiConfigManager#handleUserSwitch(int)}
+ * is ignored if the legacy store migration is not complete.
+ */
+ @Test
+ public void testHandleUserSwitchAfterBootupBeforeLegacyStoreMigration() throws Exception {
+ int user2 = TEST_DEFAULT_USER + 1;
+
+ // Switch to user2 for the first time and ensure that we don't read or
+ // write the store files.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(false);
+ mWifiConfigManager.handleUserSwitch(user2);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+ }
+
+ /**
+ * Verifies the foreground user unlock via {@link WifiConfigManager#handleUserUnlock(int)}
+ * is ignored if the legacy store migration is not complete.
+ */
+ @Test
+ public void testHandleUserUnlockAfterBootupBeforeLegacyStoreMigration() throws Exception {
+ int user1 = TEST_DEFAULT_USER;
+
+ // Unlock the user1 (default user) for the first time and ensure that we don't read or
+ // write the store files.
+ mWifiConfigManager.handleUserUnlock(user1);
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never())
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+ }
+
+ /**
+ * Verifies the private network addition using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
+ * by a non foreground user is rejected.
+ */
+ @Test
+ public void testAddNetworkUsingBackgroundUserUId() throws Exception {
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ int creatorUid = UserHandle.getUid(user2, 674);
+
+ // Create a network for user2 try adding it. This should be rejected.
+ final WifiConfiguration user2Network = WifiConfigurationTestUtil.createPskNetwork();
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(user2Network, creatorUid);
+ assertFalse(result.isSuccess());
+ }
+
+ /**
+ * Verifies the private network addition using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)}
+ * by SysUI is always accepted.
+ */
+ @Test
+ public void testAddNetworkUsingSysUiUid() throws Exception {
+ // Set up the user profiles stuff. Needed for |WifiConfigurationUtil.isVisibleToAnyProfile|
+ int user2 = TEST_DEFAULT_USER + 1;
+ setupUserProfiles(user2);
+
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(false);
+ mWifiConfigManager.handleUserSwitch(user2);
+
+ // Create a network for user2 try adding it. This should be rejected.
+ final WifiConfiguration user2Network = WifiConfigurationTestUtil.createPskNetwork();
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(user2Network, TEST_SYSUI_UID);
+ assertTrue(result.isSuccess());
+ }
+
+ /**
+ * Verifies the loading of networks using {@link WifiConfigManager#migrateFromLegacyStore()} ()}
+ * attempts to migrate data from legacy stores when the legacy store files are present.
+ */
+ @Test
+ public void testMigrationFromLegacyStore() throws Exception {
+ // Create the store data to be returned from legacy stores.
+ List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(WifiConfigurationTestUtil.createPskNetwork());
+ networks.add(WifiConfigurationTestUtil.createEapNetwork());
+ networks.add(WifiConfigurationTestUtil.createWepNetwork());
+ String deletedEphemeralSSID = "EphemeralSSID";
+ Set<String> deletedEphermalSSIDs = new HashSet<>(Arrays.asList(deletedEphemeralSSID));
+ WifiConfigStoreDataLegacy storeData =
+ new WifiConfigStoreDataLegacy(networks, deletedEphermalSSIDs);
+
+ when(mWifiConfigStoreLegacy.areStoresPresent()).thenReturn(true);
+ when(mWifiConfigStoreLegacy.read()).thenReturn(storeData);
+
+ // Now trigger the migration from legacy store. This should populate the in memory list with
+ // all the networks above from the legacy store.
+ assertTrue(mWifiConfigManager.migrateFromLegacyStore());
+
+ verify(mWifiConfigStoreLegacy).read();
+ verify(mWifiConfigStoreLegacy).removeStores();
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+ assertTrue(mWifiConfigManager.wasEphemeralNetworkDeleted(deletedEphemeralSSID));
+ }
+
+ /**
+ * Verifies the loading of networks using {@link WifiConfigManager#migrateFromLegacyStore()} ()}
+ * does not attempt to migrate data from legacy stores when the legacy store files are absent
+ * (i.e migration was already done once).
+ */
+ @Test
+ public void testNoDuplicateMigrationFromLegacyStore() throws Exception {
+ when(mWifiConfigStoreLegacy.areStoresPresent()).thenReturn(false);
+
+ // Now trigger a migration from legacy store.
+ assertTrue(mWifiConfigManager.migrateFromLegacyStore());
+
+ verify(mWifiConfigStoreLegacy, never()).read();
+ verify(mWifiConfigStoreLegacy, never()).removeStores();
+ }
+
+ /**
+ * Verifies the loading of networks using {@link WifiConfigManager#loadFromStore()} does
+ * not attempt to read from any of the stores (new or legacy) when the store files are
+ * not present.
+ */
+ @Test
+ public void testFreshInstallDoesNotLoadFromStore() throws Exception {
+ when(mWifiConfigStore.areStoresPresent()).thenReturn(false);
+ when(mWifiConfigStoreLegacy.areStoresPresent()).thenReturn(false);
+
+ assertTrue(mWifiConfigManager.loadFromStore());
+
+ verify(mWifiConfigStore, never()).read();
+ verify(mWifiConfigStoreLegacy, never()).read();
+
+ assertTrue(mWifiConfigManager.getConfiguredNetworksWithPasswords().isEmpty());
+ }
+
+ /**
+ * Verifies the user switch using {@link WifiConfigManager#handleUserSwitch(int)} is handled
+ * when the store files (new or legacy) are not present.
+ */
+ @Test
+ public void testHandleUserSwitchAfterFreshInstall() throws Exception {
+ int user2 = TEST_DEFAULT_USER + 1;
+ when(mWifiConfigStore.areStoresPresent()).thenReturn(false);
+ when(mWifiConfigStoreLegacy.areStoresPresent()).thenReturn(false);
+
+ assertTrue(mWifiConfigManager.loadFromStore());
+ verify(mWifiConfigStore, never()).read();
+ verify(mWifiConfigStoreLegacy, never()).read();
+
+ setupStoreDataForUserRead(new ArrayList<WifiConfiguration>(), new HashSet<String>());
+ // Now switch the user to user 2.
+ when(mUserManager.isUserUnlockingOrUnlocked(user2)).thenReturn(true);
+ mWifiConfigManager.handleUserSwitch(user2);
+ // Ensure that the read was invoked.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore)
+ .switchUserStoreAndRead(any(WifiConfigStore.StoreFile.class));
+ }
+
+ /**
+ * Verifies that the last user selected network parameter is set when
+ * {@link WifiConfigManager#enableNetwork(int, boolean, int)} with disableOthers flag is set
+ * to true and cleared when either {@link WifiConfigManager#disableNetwork(int, int)} or
+ * {@link WifiConfigManager#removeNetwork(int, int)} is invoked using the same network ID.
+ */
+ @Test
+ public void testLastSelectedNetwork() throws Exception {
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(67L);
+ assertTrue(mWifiConfigManager.enableNetwork(
+ result.getNetworkId(), true, TEST_CREATOR_UID));
+ assertEquals(result.getNetworkId(), mWifiConfigManager.getLastSelectedNetwork());
+ assertEquals(67, mWifiConfigManager.getLastSelectedTimeStamp());
+
+ // Now disable the network and ensure that the last selected flag is cleared.
+ assertTrue(mWifiConfigManager.disableNetwork(result.getNetworkId(), TEST_CREATOR_UID));
+ assertEquals(
+ WifiConfiguration.INVALID_NETWORK_ID, mWifiConfigManager.getLastSelectedNetwork());
+
+ // Enable it again and remove the network to ensure that the last selected flag was cleared.
+ assertTrue(mWifiConfigManager.enableNetwork(
+ result.getNetworkId(), true, TEST_CREATOR_UID));
+ assertEquals(result.getNetworkId(), mWifiConfigManager.getLastSelectedNetwork());
+ assertEquals(openNetwork.configKey(), mWifiConfigManager.getLastSelectedNetworkConfigKey());
+
+ assertTrue(mWifiConfigManager.removeNetwork(result.getNetworkId(), TEST_CREATOR_UID));
+ assertEquals(
+ WifiConfiguration.INVALID_NETWORK_ID, mWifiConfigManager.getLastSelectedNetwork());
+ }
+
+ /**
+ * Verifies that all the networks for the provided app is removed when
+ * {@link WifiConfigManager#removeNetworksForApp(ApplicationInfo)} is invoked.
+ */
+ @Test
+ public void testRemoveNetworksForApp() throws Exception {
+ verifyAddNetworkToWifiConfigManager(WifiConfigurationTestUtil.createOpenNetwork());
+ verifyAddNetworkToWifiConfigManager(WifiConfigurationTestUtil.createPskNetwork());
+ verifyAddNetworkToWifiConfigManager(WifiConfigurationTestUtil.createWepNetwork());
+
+ assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+
+ ApplicationInfo app = new ApplicationInfo();
+ app.uid = TEST_CREATOR_UID;
+ app.packageName = TEST_CREATOR_NAME;
+ assertEquals(3, mWifiConfigManager.removeNetworksForApp(app).size());
+
+ // Ensure all the networks are removed now.
+ assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ }
+
+ /**
+ * Verifies that all the networks for the provided user is removed when
+ * {@link WifiConfigManager#removeNetworksForUser(int)} is invoked.
+ */
+ @Test
+ public void testRemoveNetworksForUser() throws Exception {
+ verifyAddNetworkToWifiConfigManager(WifiConfigurationTestUtil.createOpenNetwork());
+ verifyAddNetworkToWifiConfigManager(WifiConfigurationTestUtil.createPskNetwork());
+ verifyAddNetworkToWifiConfigManager(WifiConfigurationTestUtil.createWepNetwork());
+
+ assertFalse(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+
+ assertEquals(3, mWifiConfigManager.removeNetworksForUser(TEST_DEFAULT_USER).size());
+
+ // Ensure all the networks are removed now.
+ assertTrue(mWifiConfigManager.getConfiguredNetworks().isEmpty());
+ }
+
+ /**
+ * Verifies that the connect choice is removed from all networks when
+ * {@link WifiConfigManager#removeNetwork(int, int)} is invoked.
+ */
+ @Test
+ public void testRemoveNetworkRemovesConnectChoice() throws Exception {
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createOpenNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration network3 = WifiConfigurationTestUtil.createPskNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
+ verifyAddNetworkToWifiConfigManager(network3);
+
+ // Set connect choice of network 2 over network 1.
+ assertTrue(
+ mWifiConfigManager.setNetworkConnectChoice(
+ network1.networkId, network2.configKey(), 78L));
+
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(network1.networkId);
+ assertEquals(
+ network2.configKey(),
+ retrievedNetwork.getNetworkSelectionStatus().getConnectChoice());
+
+ // Remove network 3 and ensure that the connect choice on network 1 is not removed.
+ assertTrue(mWifiConfigManager.removeNetwork(network3.networkId, TEST_CREATOR_UID));
+ retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(network1.networkId);
+ assertEquals(
+ network2.configKey(),
+ retrievedNetwork.getNetworkSelectionStatus().getConnectChoice());
+
+ // Now remove network 2 and ensure that the connect choice on network 1 is removed..
+ assertTrue(mWifiConfigManager.removeNetwork(network2.networkId, TEST_CREATOR_UID));
+ retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(network1.networkId);
+ assertNotEquals(
+ network2.configKey(),
+ retrievedNetwork.getNetworkSelectionStatus().getConnectChoice());
+
+ // This should have triggered 2 buffered writes. 1 for setting the connect choice, 1 for
+ // clearing it after network removal.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, times(2)).write(eq(false));
+ }
+
+ /**
+ * Verifies that the modification of a single network using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} and ensures that any
+ * updates to the network config in
+ * {@link WifiKeyStore#updateNetworkKeys(WifiConfiguration, WifiConfiguration)} is reflected
+ * in the internal database.
+ */
+ @Test
+ public void testUpdateSingleNetworkWithKeysUpdate() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createEapNetwork();
+ network.enterpriseConfig =
+ WifiConfigurationTestUtil.createPEAPWifiEnterpriseConfigWithGTCPhase2();
+ verifyAddNetworkToWifiConfigManager(network);
+
+ // Now verify that network configurations match before we make any change.
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network,
+ mWifiConfigManager.getConfiguredNetworkWithPassword(network.networkId));
+
+ // Modify the network ca_cert field in updateNetworkKeys method during a network
+ // config update.
+ final String newCaCertAlias = "test";
+ assertNotEquals(newCaCertAlias, network.enterpriseConfig.getCaCertificateAlias());
+
+ doAnswer(new AnswerWithArguments() {
+ public boolean answer(WifiConfiguration newConfig, WifiConfiguration existingConfig) {
+ newConfig.enterpriseConfig.setCaCertificateAlias(newCaCertAlias);
+ return true;
+ }
+ }).when(mWifiKeyStore).updateNetworkKeys(
+ any(WifiConfiguration.class), any(WifiConfiguration.class));
+
+ verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(network);
+
+ // Now verify that the keys update is reflected in the configuration fetched from internal
+ // db.
+ network.enterpriseConfig.setCaCertificateAlias(newCaCertAlias);
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network,
+ mWifiConfigManager.getConfiguredNetworkWithPassword(network.networkId));
+ }
+
+ /**
+ * Verifies that the dump method prints out all the saved network details with passwords masked.
+ * {@link WifiConfigManager#dump(FileDescriptor, PrintWriter, String[])}.
+ */
+ @Test
+ public void testDump() {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork();
+ eapNetwork.enterpriseConfig.setPassword("blah");
+
+ verifyAddNetworkToWifiConfigManager(pskNetwork);
+ verifyAddNetworkToWifiConfigManager(eapNetwork);
+
+ StringWriter stringWriter = new StringWriter();
+ mWifiConfigManager.dump(
+ new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
+ String dumpString = stringWriter.toString();
+
+ // Ensure that the network SSIDs were dumped out.
+ assertTrue(dumpString.contains(pskNetwork.SSID));
+ assertTrue(dumpString.contains(eapNetwork.SSID));
+
+ // Ensure that the network passwords were not dumped out.
+ assertFalse(dumpString.contains(pskNetwork.preSharedKey));
+ assertFalse(dumpString.contains(eapNetwork.enterpriseConfig.getPassword()));
+ }
+
+ /**
+ * Verifies the ordering of network list generated using
+ * {@link WifiConfigManager#retrieveHiddenNetworkList()}.
+ */
+ @Test
+ public void testRetrieveHiddenList() {
+ // Create and add 3 networks.
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createWepHiddenNetwork();
+ WifiConfiguration network2 = WifiConfigurationTestUtil.createPskHiddenNetwork();
+ WifiConfiguration network3 = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+ verifyAddNetworkToWifiConfigManager(network1);
+ verifyAddNetworkToWifiConfigManager(network2);
+ verifyAddNetworkToWifiConfigManager(network3);
+
+ // Enable all of them.
+ assertTrue(mWifiConfigManager.enableNetwork(network1.networkId, false, TEST_CREATOR_UID));
+ assertTrue(mWifiConfigManager.enableNetwork(network2.networkId, false, TEST_CREATOR_UID));
+ assertTrue(mWifiConfigManager.enableNetwork(network3.networkId, false, TEST_CREATOR_UID));
+
+ // Now set scan results in 2 of them to set the corresponding
+ // {@link NetworkSelectionStatus#mSeenInLastQualifiedNetworkSelection} field.
+ assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(
+ network1.networkId, createScanDetailForNetwork(network1).getScanResult(), 54));
+ assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(
+ network3.networkId, createScanDetailForNetwork(network3).getScanResult(), 54));
+
+ // Now increment |network3|'s association count. This should ensure that this network
+ // is preferred over |network1|.
+ assertTrue(mWifiConfigManager.updateNetworkAfterConnect(network3.networkId));
+
+ // Retrieve the hidden network list & verify the order of the networks returned.
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks =
+ mWifiConfigManager.retrieveHiddenNetworkList();
+ assertEquals(3, hiddenNetworks.size());
+ assertEquals(network3.SSID, hiddenNetworks.get(0).ssid);
+ assertEquals(network1.SSID, hiddenNetworks.get(1).ssid);
+ assertEquals(network2.SSID, hiddenNetworks.get(2).ssid);
+
+ // Now permanently disable |network3|. This should remove network 3 from the list.
+ assertTrue(mWifiConfigManager.disableNetwork(network3.networkId, TEST_CREATOR_UID));
+
+ // Retrieve the hidden network list again & verify the order of the networks returned.
+ hiddenNetworks = mWifiConfigManager.retrieveHiddenNetworkList();
+ assertEquals(2, hiddenNetworks.size());
+ assertEquals(network1.SSID, hiddenNetworks.get(0).ssid);
+ assertEquals(network2.SSID, hiddenNetworks.get(1).ssid);
+ }
+
+ /**
+ * Verifies the addition of network configurations using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} with same SSID and
+ * default key mgmt does not add duplicate network configs.
+ */
+ @Test
+ public void testAddMultipleNetworksWithSameSSIDAndDefaultKeyMgmt() {
+ final String ssid = "test_blah";
+ // Add a network with the above SSID and default key mgmt and ensure it was added
+ // successfully.
+ WifiConfiguration network1 = new WifiConfiguration();
+ network1.SSID = ssid;
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(network1);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertTrue(result.isNewNetwork());
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ assertEquals(1, retrievedNetworks.size());
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network1, retrievedNetworks.get(0));
+
+ // Now add a second network with the same SSID and default key mgmt and ensure that it
+ // didn't add a new duplicate network.
+ WifiConfiguration network2 = new WifiConfiguration();
+ network2.SSID = ssid;
+ result = addNetworkToWifiConfigManager(network2);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertFalse(result.isNewNetwork());
+
+ retrievedNetworks = mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ assertEquals(1, retrievedNetworks.size());
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network2, retrievedNetworks.get(0));
+ }
+
+ /**
+ * Verifies the addition of network configurations using
+ * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} with same SSID and
+ * different key mgmt should add different network configs.
+ */
+ @Test
+ public void testAddMultipleNetworksWithSameSSIDAndDifferentKeyMgmt() {
+ final String ssid = "test_blah";
+ // Add a network with the above SSID and WPA_PSK key mgmt and ensure it was added
+ // successfully.
+ WifiConfiguration network1 = new WifiConfiguration();
+ network1.SSID = ssid;
+ network1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(network1);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertTrue(result.isNewNetwork());
+
+ List<WifiConfiguration> retrievedNetworks =
+ mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ assertEquals(1, retrievedNetworks.size());
+ WifiConfigurationTestUtil.assertConfigurationEqualForConfigManagerAddOrUpdate(
+ network1, retrievedNetworks.get(0));
+
+ // Now add a second network with the same SSID and NONE key mgmt and ensure that it
+ // does add a new network.
+ WifiConfiguration network2 = new WifiConfiguration();
+ network2.SSID = ssid;
+ network2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ result = addNetworkToWifiConfigManager(network2);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertTrue(result.isNewNetwork());
+
+ retrievedNetworks = mWifiConfigManager.getConfiguredNetworksWithPasswords();
+ assertEquals(2, retrievedNetworks.size());
+ List<WifiConfiguration> networks = Arrays.asList(network1, network2);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigManagerAddOrUpdate(
+ networks, retrievedNetworks);
+ }
+
+ /**
+ * Verifies that adding a network with a proxy, without having permission OVERRIDE_WIFI_CONFIG,
+ * holding device policy, or profile owner policy fails.
+ */
+ @Test
+ public void testAddNetworkWithProxyFails() {
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
+ false, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
+ false, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ }
+
+ /**
+ * Verifies that adding a network with a PAC or STATIC proxy with permission
+ * OVERRIDE_WIFI_CONFIG is successful
+ */
+ @Test
+ public void testAddNetworkWithProxyWithConfOverride() {
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ true, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ true, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ }
+
+ /**
+ * Verifies that adding a network with a PAC or STATIC proxy, while holding policy
+ * {@link DeviceAdminInfo.USES_POLICY_PROFILE_OWNER} is successful
+ */
+ @Test
+ public void testAddNetworkWithProxyAsProfileOwner() {
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ }
+ /**
+ * Verifies that adding a network with a PAC or STATIC proxy, while holding policy
+ * {@link DeviceAdminInfo.USES_POLICY_DEVICE_OWNER} is successful
+ */
+ @Test
+ public void testAddNetworkWithProxyAsDeviceOwner() {
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ true, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ true, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ }
+ /**
+ * Verifies that updating a network (that has no proxy) and adding a PAC or STATIC proxy fails
+ * without being able to override configs, or holding Device or Profile owner policies.
+ */
+ @Test
+ public void testUpdateNetworkAddProxyFails() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(network);
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
+ false, // assertSuccess
+ result.getNetworkId()); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithStaticProxy(),
+ false, // assertSuccess
+ result.getNetworkId()); // Update networkID
+ }
+ /**
+ * Verifies that updating a network and adding a proxy is successful in the cases where app can
+ * override configs, holds policy {@link DeviceAdminInfo.USES_POLICY_PROFILE_OWNER},
+ * and holds policy {@link DeviceAdminInfo.USES_POLICY_DEVICE_OWNER}, and that it fails
+ * otherwise.
+ */
+ @Test
+ public void testUpdateNetworkAddProxyWithPermissionAndSystem() {
+ // Testing updating network with uid permission OVERRIDE_WIFI_CONFIG
+ WifiConfiguration network = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(network, TEST_CREATOR_UID);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ true, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+
+ // Testing updating network with proxy while holding Profile Owner policy
+ network = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+ result = addNetworkToWifiConfigManager(network, TEST_NO_PERM_UID);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+
+ // Testing updating network with proxy while holding Device Owner Policy
+ network = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+ result = addNetworkToWifiConfigManager(network, TEST_NO_PERM_UID);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ true, // withDeviceOwnerPolicy
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy(),
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+ }
+
+ /**
+ * Verifies that updating a network that has a proxy without changing the proxy, can succeed
+ * without proxy specific permissions.
+ */
+ @Test
+ public void testUpdateNetworkUnchangedProxy() {
+ IpConfiguration ipConf = WifiConfigurationTestUtil.createDHCPIpConfigurationWithPacProxy();
+ // First create a WifiConfiguration with proxy
+ NetworkUpdateResult result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf,
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ // Update the network while using the same ipConf, and no proxy specific permissions
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf,
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+ }
+
+ /**
+ * Verifies that updating a network with a different proxy succeeds in the cases where app can
+ * override configs, holds policy {@link DeviceAdminInfo.USES_POLICY_PROFILE_OWNER},
+ * and holds policy {@link DeviceAdminInfo.USES_POLICY_DEVICE_OWNER}, and that it fails
+ * otherwise.
+ */
+ @Test
+ public void testUpdateNetworkDifferentProxy() {
+ // Create two proxy configurations of the same type, but different values
+ IpConfiguration ipConf1 =
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithSpecificProxy(
+ WifiConfigurationTestUtil.STATIC_PROXY_SETTING,
+ TEST_STATIC_PROXY_HOST_1,
+ TEST_STATIC_PROXY_PORT_1,
+ TEST_STATIC_PROXY_EXCLUSION_LIST_1,
+ TEST_PAC_PROXY_LOCATION_1);
+ IpConfiguration ipConf2 =
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithSpecificProxy(
+ WifiConfigurationTestUtil.STATIC_PROXY_SETTING,
+ TEST_STATIC_PROXY_HOST_2,
+ TEST_STATIC_PROXY_PORT_2,
+ TEST_STATIC_PROXY_EXCLUSION_LIST_2,
+ TEST_PAC_PROXY_LOCATION_2);
+
+ // Update with Conf Override
+ NetworkUpdateResult result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ true, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf1,
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ true, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf2,
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+
+ // Update as Device Owner
+ result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ true, // withDeviceOwnerPolicy
+ ipConf1,
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ true, // withDeviceOwnerPolicy
+ ipConf2,
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+
+ // Update as Profile Owner
+ result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf1,
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf2,
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+
+ // Update with no permissions (should fail)
+ result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf1,
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf2,
+ false, // assertSuccess
+ result.getNetworkId()); // Update networkID
+ }
+ /**
+ * Verifies that updating a network removing its proxy succeeds in the cases where app can
+ * override configs, holds policy {@link DeviceAdminInfo.USES_POLICY_PROFILE_OWNER},
+ * and holds policy {@link DeviceAdminInfo.USES_POLICY_DEVICE_OWNER}, and that it fails
+ * otherwise.
+ */
+ @Test
+ public void testUpdateNetworkRemoveProxy() {
+ // Create two different IP configurations, one with a proxy and another without.
+ IpConfiguration ipConf1 =
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithSpecificProxy(
+ WifiConfigurationTestUtil.STATIC_PROXY_SETTING,
+ TEST_STATIC_PROXY_HOST_1,
+ TEST_STATIC_PROXY_PORT_1,
+ TEST_STATIC_PROXY_EXCLUSION_LIST_1,
+ TEST_PAC_PROXY_LOCATION_1);
+ IpConfiguration ipConf2 =
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithSpecificProxy(
+ WifiConfigurationTestUtil.NONE_PROXY_SETTING,
+ TEST_STATIC_PROXY_HOST_2,
+ TEST_STATIC_PROXY_PORT_2,
+ TEST_STATIC_PROXY_EXCLUSION_LIST_2,
+ TEST_PAC_PROXY_LOCATION_2);
+
+ // Update with Conf Override
+ NetworkUpdateResult result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ true, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf1,
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ true, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf2,
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+
+ // Update as Device Owner
+ result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ true, // withDeviceOwnerPolicy
+ ipConf1,
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ true, // withDeviceOwnerPolicy
+ ipConf2,
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+
+ // Update as Profile Owner
+ result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf1,
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf2,
+ true, // assertSuccess
+ result.getNetworkId()); // Update networkID
+
+ // Update with no permissions (should fail)
+ result = verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ true, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf1,
+ true, // assertSuccess
+ WifiConfiguration.INVALID_NETWORK_ID); // Update networkID
+ verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ false, // withConfOverride
+ false, // withProfileOwnerPolicy
+ false, // withDeviceOwnerPolicy
+ ipConf2,
+ false, // assertSuccess
+ result.getNetworkId()); // Update networkID
+ }
+
+ /**
+ * Verifies that the app specified BSSID is converted and saved in lower case.
+ */
+ @Test
+ public void testAppSpecifiedBssidIsSavedInLowerCase() {
+ final String bssid = "0A:08:5C:BB:89:6D"; // upper case
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ openNetwork.BSSID = bssid;
+
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(openNetwork);
+
+ WifiConfiguration retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(
+ result.getNetworkId());
+
+ assertNotEquals(retrievedNetwork.BSSID, bssid);
+ assertEquals(retrievedNetwork.BSSID, bssid.toLowerCase());
+ }
+
+ private NetworkUpdateResult verifyAddOrUpdateNetworkWithProxySettingsAndPermissions(
+ boolean withConfOverride,
+ boolean withProfileOwnerPolicy,
+ boolean withDeviceOwnerPolicy,
+ IpConfiguration ipConfiguration,
+ boolean assertSuccess,
+ int networkId) {
+ WifiConfiguration network;
+ if (networkId == WifiConfiguration.INVALID_NETWORK_ID) {
+ network = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+ } else {
+ network = mWifiConfigManager.getConfiguredNetwork(networkId);
+ }
+ network.setIpConfiguration(ipConfiguration);
+ when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(),
+ eq(DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)))
+ .thenReturn(withProfileOwnerPolicy);
+ when(mDevicePolicyManagerInternal.isActiveAdminWithPolicy(anyInt(),
+ eq(DeviceAdminInfo.USES_POLICY_DEVICE_OWNER)))
+ .thenReturn(withDeviceOwnerPolicy);
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt()))
+ .thenReturn(withConfOverride);
+ int uid = withConfOverride ? TEST_CREATOR_UID : TEST_NO_PERM_UID;
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(network, uid);
+ assertEquals(assertSuccess, result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ return result;
+ }
+
+ private void createWifiConfigManager() {
+ mWifiConfigManager =
+ new WifiConfigManager(
+ mContext, mClock, mUserManager, mTelephonyManager,
+ mWifiKeyStore, mWifiConfigStore, mWifiConfigStoreLegacy,
+ mWifiPermissionsUtil, mWifiPermissionsWrapper, mNetworkListStoreData,
+ mDeletedEphemeralSsidsStoreData);
+ mWifiConfigManager.enableVerboseLogging(1);
+ }
+
+ /**
+ * 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);
+ }
+ 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);
+ }
+ configuration.status = WifiConfiguration.Status.DISABLED;
+ configuration.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
+ }
+
+ /**
+ * 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 =
+ WifiConfigManager.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 =
+ WifiConfigManager.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);
+ }
+
+ /**
+ * Helper method to capture the networks list store data that will be written by
+ * WifiConfigStore.write() method.
+ */
+ private Pair<List<WifiConfiguration>, List<WifiConfiguration>>
+ captureWriteNetworksListStoreData() {
+ try {
+ ArgumentCaptor<ArrayList> sharedConfigsCaptor =
+ ArgumentCaptor.forClass(ArrayList.class);
+ ArgumentCaptor<ArrayList> userConfigsCaptor =
+ ArgumentCaptor.forClass(ArrayList.class);
+ mNetworkListStoreDataMockOrder.verify(mNetworkListStoreData)
+ .setSharedConfigurations(sharedConfigsCaptor.capture());
+ mNetworkListStoreDataMockOrder.verify(mNetworkListStoreData)
+ .setUserConfigurations(userConfigsCaptor.capture());
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore).write(anyBoolean());
+ return Pair.create(sharedConfigsCaptor.getValue(), userConfigsCaptor.getValue());
+ } catch (Exception e) {
+ fail("Exception encountered during write " + e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the provided network was in the store data or not.
+ */
+ private boolean isNetworkInConfigStoreData(WifiConfiguration configuration) {
+ Pair<List<WifiConfiguration>, List<WifiConfiguration>> networkListStoreData =
+ captureWriteNetworksListStoreData();
+ if (networkListStoreData == null) {
+ return false;
+ }
+ List<WifiConfiguration> networkList = new ArrayList<>();
+ networkList.addAll(networkListStoreData.first);
+ networkList.addAll(networkListStoreData.second);
+ return isNetworkInConfigStoreData(configuration, networkList);
+ }
+
+ /**
+ * Returns whether the provided network was in the store data or not.
+ */
+ private boolean isNetworkInConfigStoreData(
+ WifiConfiguration configuration, List<WifiConfiguration> networkList) {
+ boolean foundNetworkInStoreData = false;
+ for (WifiConfiguration retrievedConfig : networkList) {
+ if (retrievedConfig.configKey().equals(configuration.configKey())) {
+ foundNetworkInStoreData = true;
+ break;
+ }
+ }
+ return foundNetworkInStoreData;
+ }
+
+ /**
+ * Setup expectations for WifiNetworksListStoreData and DeletedEphemeralSsidsStoreData
+ * after WifiConfigStore#read.
+ */
+ private void setupStoreDataForRead(List<WifiConfiguration> sharedConfigurations,
+ List<WifiConfiguration> userConfigurations, Set<String> deletedEphemeralSsids) {
+ when(mNetworkListStoreData.getSharedConfigurations())
+ .thenReturn(sharedConfigurations);
+ when(mNetworkListStoreData.getUserConfigurations()).thenReturn(userConfigurations);
+ when(mDeletedEphemeralSsidsStoreData.getSsidList()).thenReturn(deletedEphemeralSsids);
+ }
+
+ /**
+ * Setup expectations for WifiNetworksListStoreData and DeletedEphemeralSsidsStoreData
+ * after WifiConfigStore#switchUserStoreAndRead.
+ */
+ private void setupStoreDataForUserRead(List<WifiConfiguration> userConfigurations,
+ Set<String> deletedEphemeralSsids) {
+ when(mNetworkListStoreData.getUserConfigurations()).thenReturn(userConfigurations);
+ when(mDeletedEphemeralSsidsStoreData.getSsidList()).thenReturn(deletedEphemeralSsids);
+ }
+
+ /**
+ * 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(WifiConfigManager.PASSWORD_MASK, configuration.preSharedKey);
+ }
+ if (configuration.wepKeys != null) {
+ for (int i = 0; i < configuration.wepKeys.length; i++) {
+ if (!TextUtils.isEmpty(configuration.wepKeys[i])) {
+ assertEquals(WifiConfigManager.PASSWORD_MASK, configuration.wepKeys[i]);
+ }
+ }
+ }
+ if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) {
+ assertEquals(
+ WifiConfigManager.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);
+ }
+
+ private void verifyWifiConfigStoreRead() {
+ assertTrue(mWifiConfigManager.loadFromStore());
+ mContextConfigStoreMockOrder.verify(mContext)
+ .sendBroadcastAsUser(any(Intent.class), any(UserHandle.class));
+ }
+
+ private void triggerStoreReadIfNeeded() {
+ // Trigger a store read if not already done.
+ if (!mStoreReadTriggered) {
+ verifyWifiConfigStoreRead();
+ mStoreReadTriggered = true;
+ }
+ }
+
+ /**
+ * Adds the provided configuration to WifiConfigManager with uid = TEST_CREATOR_UID.
+ */
+ private NetworkUpdateResult addNetworkToWifiConfigManager(WifiConfiguration configuration) {
+ return addNetworkToWifiConfigManager(configuration, TEST_CREATOR_UID);
+ }
+
+ /**
+ * 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.
+ * This method also triggers a store read if not already done.
+ */
+ private NetworkUpdateResult addNetworkToWifiConfigManager(WifiConfiguration configuration,
+ int uid) {
+ triggerStoreReadIfNeeded();
+ when(mClock.getWallClockMillis()).thenReturn(TEST_WALLCLOCK_CREATION_TIME_MILLIS);
+ NetworkUpdateResult result =
+ mWifiConfigManager.addOrUpdateNetwork(configuration, 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) throws Exception {
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(configuration);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertTrue(result.isNewNetwork());
+ assertTrue(result.hasIpChanged());
+ assertTrue(result.hasProxyChanged());
+
+ verifyNetworkAddBroadcast(configuration);
+ // Ensure that the write was not invoked for ephemeral network addition.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+ return result;
+ }
+
+ /**
+ * Add Passpoint network to WifiConfigManager and ensure that it was successful.
+ */
+ private NetworkUpdateResult verifyAddPasspointNetworkToWifiConfigManager(
+ WifiConfiguration configuration) throws Exception {
+ NetworkUpdateResult result = addNetworkToWifiConfigManager(configuration);
+ assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID);
+ assertTrue(result.isNewNetwork());
+ assertTrue(result.hasIpChanged());
+ assertTrue(result.hasProxyChanged());
+
+ // Verify keys are not being installed.
+ verify(mWifiKeyStore, never()).updateNetworkKeys(any(WifiConfiguration.class),
+ any(WifiConfiguration.class));
+ verifyNetworkAddBroadcast(configuration);
+ // Ensure that the write was not invoked for Passpoint network addition.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+ 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, TEST_CREATOR_UID));
+
+ verifyNetworkRemoveBroadcast(configuration);
+ // Verify if the config store write was triggered without this new configuration.
+ verifyNetworkNotInConfigStoreData(configuration);
+ }
+
+ /**
+ * Removes ephemeral network from WifiConfigManager and ensure that it was successful.
+ */
+ private void verifyRemoveEphemeralNetworkFromWifiConfigManager(
+ WifiConfiguration configuration) throws Exception {
+ assertTrue(mWifiConfigManager.removeNetwork(configuration.networkId, TEST_CREATOR_UID));
+
+ verifyNetworkRemoveBroadcast(configuration);
+ // Ensure that the write was not invoked for ephemeral network remove.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+ }
+
+ /**
+ * Removes Passpoint network from WifiConfigManager and ensure that it was successful.
+ */
+ private void verifyRemovePasspointNetworkFromWifiConfigManager(
+ WifiConfiguration configuration) throws Exception {
+ assertTrue(mWifiConfigManager.removeNetwork(configuration.networkId, TEST_CREATOR_UID));
+
+ // Verify keys are not being removed.
+ verify(mWifiKeyStore, never()).removeKeys(any(WifiEnterpriseConfig.class));
+ verifyNetworkRemoveBroadcast(configuration);
+ // Ensure that the write was not invoked for Passpoint network remove.
+ mContextConfigStoreMockOrder.verify(mWifiConfigStore, never()).write(anyBoolean());
+ }
+
+ /**
+ * 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 =
+ WifiConfigManager.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 given BSSID, level &frequency
+ * values.
+ */
+ private ScanDetail createScanDetailForNetwork(
+ WifiConfiguration configuration, String bssid, int level, int frequency) {
+ String caps;
+ if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+ caps = "[WPA2-PSK-CCMP]";
+ } else if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
+ || configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
+ caps = "[WPA2-EAP-CCMP]";
+ } else if (configuration.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)
+ && WifiConfigurationUtil.hasAnyValidWepKey(configuration.wepKeys)) {
+ caps = "[WEP]";
+ } else {
+ caps = "[]";
+ }
+ WifiSsid ssid = WifiSsid.createFromAsciiEncoded(configuration.getPrintableSsid());
+ // Fill in 0's in the fields we don't care about.
+ return new ScanDetail(
+ ssid, bssid, caps, level, frequency, mClock.getUptimeSinceBootMillis(),
+ mClock.getWallClockMillis());
+ }
+
+ /**
+ * Creates a scan detail corresponding to the provided network and BSSID value.
+ */
+ private ScanDetail createScanDetailForNetwork(WifiConfiguration configuration, String bssid) {
+ return createScanDetailForNetwork(configuration, bssid, 0, 0);
+ }
+
+ /**
+ * 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 WifiConfigManager#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);
+ // Retrieve the network with password data for comparison.
+ retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetworkWithPassword(retrievedNetwork.networkId);
+
+ 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);
+ }
+
+ /**
+ * Adds a new network and verifies that the |HasEverConnected| flag is set to false.
+ */
+ private void verifyAddNetworkHasEverConnectedFalse(WifiConfiguration network) {
+ NetworkUpdateResult result = verifyAddNetworkToWifiConfigManager(network);
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ assertFalse("Adding a new network should not have hasEverConnected set to true.",
+ retrievedNetwork.getNetworkSelectionStatus().getHasEverConnected());
+ }
+
+ /**
+ * Updates an existing network with some credential change and verifies that the
+ * |HasEverConnected| flag is set to false.
+ */
+ private void verifyUpdateNetworkWithCredentialChangeHasEverConnectedFalse(
+ WifiConfiguration network) {
+ NetworkUpdateResult result = verifyUpdateNetworkToWifiConfigManagerWithoutIpChange(network);
+ WifiConfiguration retrievedNetwork =
+ mWifiConfigManager.getConfiguredNetwork(result.getNetworkId());
+ assertFalse("Updating network credentials config should clear hasEverConnected.",
+ retrievedNetwork.getNetworkSelectionStatus().getHasEverConnected());
+ assertTrue(result.hasCredentialChanged());
+ }
+
+ /**
+ * Updates an existing network after connection using
+ * {@link WifiConfigManager#updateNetworkAfterConnect(int)} and asserts that the
+ * |HasEverConnected| flag is set to true.
+ */
+ private void verifyUpdateNetworkAfterConnectHasEverConnectedTrue(int networkId) {
+ assertTrue(mWifiConfigManager.updateNetworkAfterConnect(networkId));
+ WifiConfiguration retrievedNetwork = mWifiConfigManager.getConfiguredNetwork(networkId);
+ assertTrue("hasEverConnected expected to be true after connection.",
+ retrievedNetwork.getNetworkSelectionStatus().getHasEverConnected());
+ }
+
+ /**
+ * Sets up a user profiles for WifiConfigManager testing.
+ *
+ * @param userId Id of the user.
+ */
+ private void setupUserProfiles(int userId) {
+ final UserInfo userInfo =
+ new UserInfo(userId, Integer.toString(userId), UserInfo.FLAG_PRIMARY);
+ List<UserInfo> userProfiles = Arrays.asList(userInfo);
+ when(mUserManager.getProfiles(userId)).thenReturn(userProfiles);
+ when(mUserManager.isUserUnlockingOrUnlocked(userId)).thenReturn(true);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreLegacyTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreLegacyTest.java
new file mode 100644
index 0000000..4b4e875
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreLegacyTest.java
@@ -0,0 +1,273 @@
+/*
+ * 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.net.IpConfiguration;
+import android.net.wifi.WifiConfiguration;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.server.net.IpConfigStore;
+import com.android.server.wifi.hotspot2.LegacyPasspointConfig;
+import com.android.server.wifi.hotspot2.LegacyPasspointConfigParser;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiConfigStoreLegacy}.
+ */
+@SmallTest
+public class WifiConfigStoreLegacyTest {
+ private static final String MASKED_FIELD_VALUE = "*";
+
+ // Test mocks
+ @Mock private WifiNative mWifiNative;
+ @Mock private WifiNetworkHistory mWifiNetworkHistory;
+ @Mock private IpConfigStore mIpconfigStore;
+ @Mock private LegacyPasspointConfigParser mPasspointConfigParser;
+
+ /**
+ * Test instance of WifiConfigStore.
+ */
+ private WifiConfigStoreLegacy mWifiConfigStore;
+
+
+ /**
+ * Setup the test environment.
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mWifiConfigStore = new WifiConfigStoreLegacy(mWifiNetworkHistory, mWifiNative,
+ mIpconfigStore, mPasspointConfigParser);
+ }
+
+ /**
+ * Called after each test
+ */
+ @After
+ public void cleanup() {
+ validateMockitoUsage();
+ }
+
+ /**
+ * Verify loading of network configurations from legacy stores. This is verifying the population
+ * of the masked wpa_supplicant fields using wpa_supplicant.conf file.
+ */
+ @Test
+ public void testLoadFromStores() throws Exception {
+ WifiConfiguration pskNetwork = WifiConfigurationTestUtil.createPskNetwork();
+ WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
+ WifiConfiguration eapNetwork = WifiConfigurationTestUtil.createEapNetwork();
+ WifiConfiguration passpointNetwork = WifiConfigurationTestUtil.createPasspointNetwork();
+ eapNetwork.enterpriseConfig.setPassword("EapPassword");
+
+ // Initialize Passpoint configuration data.
+ int passpointNetworkId = 1234;
+ String fqdn = passpointNetwork.FQDN;
+ String providerFriendlyName = passpointNetwork.providerFriendlyName;
+ long[] roamingConsortiumIds = new long[] {0x1234, 0x5678};
+ String realm = "test.com";
+ String imsi = "214321";
+
+ // Update Passpoint network.
+ // Network ID is used for lookup network extras, so use an unique ID for passpoint network.
+ passpointNetwork.networkId = passpointNetworkId;
+ passpointNetwork.enterpriseConfig.setPassword("PaspointPassword");
+ // Reset FQDN and provider friendly name so that the derived network from #read will
+ // obtained these information from networkExtras and {@link LegacyPasspointConfigParser}.
+ passpointNetwork.FQDN = null;
+ passpointNetwork.providerFriendlyName = null;
+
+ final List<WifiConfiguration> networks = new ArrayList<>();
+ networks.add(pskNetwork);
+ networks.add(wepNetwork);
+ networks.add(eapNetwork);
+ networks.add(passpointNetwork);
+
+ // Setup legacy Passpoint configuration data.
+ Map<String, LegacyPasspointConfig> passpointConfigs = new HashMap<>();
+ LegacyPasspointConfig passpointConfig = new LegacyPasspointConfig();
+ passpointConfig.mFqdn = fqdn;
+ passpointConfig.mFriendlyName = providerFriendlyName;
+ passpointConfig.mRoamingConsortiumOis = roamingConsortiumIds;
+ passpointConfig.mRealm = realm;
+ passpointConfig.mImsi = imsi;
+ passpointConfigs.put(fqdn, passpointConfig);
+
+ // Return the config data with passwords masked from wpa_supplicant control interface.
+ doAnswer(new AnswerWithArguments() {
+ public boolean answer(Map<String, WifiConfiguration> configs,
+ SparseArray<Map<String, String>> networkExtras) {
+ for (Map.Entry<String, WifiConfiguration> entry:
+ createWpaSupplicantLoadData(networks).entrySet()) {
+ configs.put(entry.getKey(), entry.getValue());
+ }
+ // Setup networkExtras for Passpoint configuration.
+ networkExtras.put(passpointNetworkId, createNetworkExtrasForPasspointConfig(fqdn));
+ return true;
+ }
+ }).when(mWifiNative).migrateNetworksFromSupplicant(any(Map.class), any(SparseArray.class));
+
+ when(mPasspointConfigParser.parseConfig(anyString())).thenReturn(passpointConfigs);
+ WifiConfigStoreLegacy.WifiConfigStoreDataLegacy storeData = mWifiConfigStore.read();
+
+ // Update the expected configuration for Passpoint network.
+ passpointNetwork.isLegacyPasspointConfig = true;
+ passpointNetwork.FQDN = fqdn;
+ passpointNetwork.providerFriendlyName = providerFriendlyName;
+ passpointNetwork.roamingConsortiumIds = roamingConsortiumIds;
+ passpointNetwork.enterpriseConfig.setRealm(realm);
+ passpointNetwork.enterpriseConfig.setPlmn(imsi);
+
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore(
+ networks, storeData.getConfigurations());
+ }
+
+ private SparseArray<IpConfiguration> createIpConfigStoreLoadData(
+ List<WifiConfiguration> configurations) {
+ SparseArray<IpConfiguration> newIpConfigurations = new SparseArray<>();
+ for (WifiConfiguration config : configurations) {
+ newIpConfigurations.put(
+ config.configKey().hashCode(),
+ new IpConfiguration(config.getIpConfiguration()));
+ }
+ return newIpConfigurations;
+ }
+
+ private Map<String, String> createPskMap(List<WifiConfiguration> configurations) {
+ Map<String, String> pskMap = new HashMap<>();
+ for (WifiConfiguration config : configurations) {
+ if (!TextUtils.isEmpty(config.preSharedKey)) {
+ pskMap.put(config.configKey(), config.preSharedKey);
+ }
+ }
+ return pskMap;
+ }
+
+ private Map<String, String> createWepKey0Map(List<WifiConfiguration> configurations) {
+ Map<String, String> wepKeyMap = new HashMap<>();
+ for (WifiConfiguration config : configurations) {
+ if (!TextUtils.isEmpty(config.wepKeys[0])) {
+ wepKeyMap.put(config.configKey(), config.wepKeys[0]);
+ }
+ }
+ return wepKeyMap;
+ }
+
+ private Map<String, String> createWepKey1Map(List<WifiConfiguration> configurations) {
+ Map<String, String> wepKeyMap = new HashMap<>();
+ for (WifiConfiguration config : configurations) {
+ if (!TextUtils.isEmpty(config.wepKeys[1])) {
+ wepKeyMap.put(config.configKey(), config.wepKeys[1]);
+ }
+ }
+ return wepKeyMap;
+ }
+
+ private Map<String, String> createWepKey2Map(List<WifiConfiguration> configurations) {
+ Map<String, String> wepKeyMap = new HashMap<>();
+ for (WifiConfiguration config : configurations) {
+ if (!TextUtils.isEmpty(config.wepKeys[2])) {
+ wepKeyMap.put(config.configKey(), config.wepKeys[2]);
+ }
+ }
+ return wepKeyMap;
+ }
+
+ private Map<String, String> createWepKey3Map(List<WifiConfiguration> configurations) {
+ Map<String, String> wepKeyMap = new HashMap<>();
+ for (WifiConfiguration config : configurations) {
+ if (!TextUtils.isEmpty(config.wepKeys[3])) {
+ wepKeyMap.put(config.configKey(), config.wepKeys[3]);
+ }
+ }
+ return wepKeyMap;
+ }
+
+ private Map<String, String> createEapPasswordMap(List<WifiConfiguration> configurations) {
+ Map<String, String> eapPasswordMap = new HashMap<>();
+ for (WifiConfiguration config : configurations) {
+ if (!TextUtils.isEmpty(config.enterpriseConfig.getPassword())) {
+ eapPasswordMap.put(config.configKey(), config.enterpriseConfig.getPassword());
+ }
+ }
+ return eapPasswordMap;
+ }
+
+ private Map<String, WifiConfiguration> createWpaSupplicantLoadData(
+ List<WifiConfiguration> configurations) {
+ Map<String, WifiConfiguration> configurationMap = new HashMap<>();
+ for (WifiConfiguration config : configurations) {
+ configurationMap.put(config.configKey(true), config);
+ }
+ return configurationMap;
+ }
+
+ private List<WifiConfiguration> createMaskedWifiConfigurations(
+ List<WifiConfiguration> configurations) {
+ List<WifiConfiguration> newConfigurations = new ArrayList<>();
+ for (WifiConfiguration config : configurations) {
+ newConfigurations.add(createMaskedWifiConfiguration(config));
+ }
+ return newConfigurations;
+ }
+
+ private WifiConfiguration createMaskedWifiConfiguration(WifiConfiguration configuration) {
+ WifiConfiguration newConfig = new WifiConfiguration(configuration);
+ if (!TextUtils.isEmpty(configuration.preSharedKey)) {
+ newConfig.preSharedKey = MASKED_FIELD_VALUE;
+ }
+ if (!TextUtils.isEmpty(configuration.wepKeys[0])) {
+ newConfig.wepKeys[0] = MASKED_FIELD_VALUE;
+ }
+ if (!TextUtils.isEmpty(configuration.wepKeys[1])) {
+ newConfig.wepKeys[1] = MASKED_FIELD_VALUE;
+ }
+ if (!TextUtils.isEmpty(configuration.wepKeys[2])) {
+ newConfig.wepKeys[2] = MASKED_FIELD_VALUE;
+ }
+ if (!TextUtils.isEmpty(configuration.wepKeys[3])) {
+ newConfig.wepKeys[3] = MASKED_FIELD_VALUE;
+ }
+ if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) {
+ newConfig.enterpriseConfig.setPassword(MASKED_FIELD_VALUE);
+ }
+ return newConfig;
+ }
+
+ private Map<String, String> createNetworkExtrasForPasspointConfig(String fqdn) {
+ Map<String, String> extras = new HashMap<>();
+ extras.put(SupplicantStaNetworkHal.ID_STRING_KEY_FQDN, fqdn);
+ return extras;
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
index 3993fe5..47efed3 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigStoreTest.java
@@ -11,233 +11,576 @@
* 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;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+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.WifiConfigStore.StoreFile;
+import com.android.server.wifi.util.XmlUtil;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
-import java.io.BufferedReader;
+import java.io.File;
import java.io.IOException;
-import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
/**
* Unit tests for {@link com.android.server.wifi.WifiConfigStore}.
*/
@SmallTest
public class WifiConfigStoreTest {
- private static final String KEY_SSID = "ssid";
- private static final String KEY_PSK = "psk";
- private static final String KEY_KEY_MGMT = "key_mgmt";
- private static final String KEY_PRIORITY = "priority";
- private static final String KEY_DISABLED = "disabled";
- private static final String KEY_ID_STR = "id_str";
- // This is not actually present as a key in the wpa_supplicant.conf file, but
- // is used in tests to conveniently access the configKey for a test network.
- private static final String CONFIG_KEY = "configKey";
+ // Store file content without any data.
+ private static final String EMPTY_FILE_CONTENT =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"1\" />\n"
+ + "</WifiConfigStoreData>\n";
- private static final HashMap<String, String> NETWORK_0_VARS = new HashMap<>();
- static {
- NETWORK_0_VARS.put(KEY_SSID, "\"TestNetwork0\"");
- NETWORK_0_VARS.put(KEY_KEY_MGMT, "NONE");
- NETWORK_0_VARS.put(KEY_PRIORITY, "2");
- NETWORK_0_VARS.put(KEY_ID_STR, ""
- + "\"%7B%22creatorUid%22%3A%221000%22%2C%22configKey%22%3A%22%5C%22"
- + "TestNetwork0%5C%22NONE%22%7D\"");
- NETWORK_0_VARS.put(CONFIG_KEY, "\"TestNetwork0\"NONE");
- }
+ private static final String TEST_USER_DATA = "UserData";
+ private static final String TEST_SHARE_DATA = "ShareData";
- private static final HashMap<String, String> NETWORK_1_VARS = new HashMap<>();
- static {
- NETWORK_1_VARS.put(KEY_SSID, "\"Test Network 1\"");
- NETWORK_1_VARS.put(KEY_KEY_MGMT, "NONE");
- NETWORK_1_VARS.put(KEY_PRIORITY, "3");
- NETWORK_1_VARS.put(KEY_DISABLED, "1");
- NETWORK_1_VARS.put(KEY_ID_STR, ""
- + "\"%7B%22creatorUid%22%3A%221000%22%2C%22configKey%22%3A%22%5C%22"
- + "Test+Network+1%5C%22NONE%22%7D\"");
- NETWORK_1_VARS.put(CONFIG_KEY, "\"Test Network 1\"NONE");
- }
+ private static final String TEST_DATA_XML_STRING_FORMAT =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"1\" />\n"
+ + "<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"
+ + "<boolean name=\"RequirePMF\" 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"
+ + "<byte-array name=\"AllowedGroupCiphers\" num=\"0\"></byte-array>\n"
+ + "<byte-array name=\"AllowedPairwiseCiphers\" num=\"0\"></byte-array>\n"
+ + "<boolean name=\"Shared\" value=\"%s\" />\n"
+ + "<int name=\"Status\" value=\"2\" />\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"
+ + "<boolean name=\"IsLegacyPasspointConfig\" value=\"false\" />\n"
+ + "<long-array name=\"RoamingConsortiumOIs\" num=\"0\" />\n"
+ + "</WifiConfiguration>\n"
+ + "<NetworkStatus>\n"
+ + "<string name=\"SelectionStatus\">NETWORK_SELECTION_ENABLED</string>\n"
+ + "<string name=\"DisableReason\">NETWORK_SELECTION_ENABLE</string>\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"
+ + "<DeletedEphemeralSSIDList>\n"
+ + "<set name=\"SSIDList\">\n"
+ + "<string>%s</string>\n"
+ + "</set>\n"
+ + "</DeletedEphemeralSSIDList>\n"
+ + "</WifiConfigStoreData>\n";
- private static final HashMap<String, String> NETWORK_2_VARS = new HashMap<>();
- static {
- NETWORK_2_VARS.put(KEY_SSID, "\"testNetwork2\"");
- NETWORK_2_VARS.put(KEY_KEY_MGMT, "NONE");
- NETWORK_2_VARS.put(KEY_PRIORITY, "4");
- NETWORK_2_VARS.put(KEY_DISABLED, "1");
- NETWORK_2_VARS.put(KEY_ID_STR, ""
- + "\"%7B%22creatorUid%22%3A%221000%22%2C%22configKey%22%3A%22%5C%22"
- + "testNetwork2%5C%22NONE%22%7D\"");
- NETWORK_2_VARS.put(CONFIG_KEY, "\"testNetwork2\"NONE");
- }
-
- private static final HashMap<String, String> NETWORK_3_VARS = new HashMap<>();
- static {
- NETWORK_3_VARS.put(KEY_SSID, "\"testwpa2psk\"");
- NETWORK_3_VARS.put(KEY_PSK, "blahblah");
- NETWORK_3_VARS.put(KEY_KEY_MGMT, "WPA-PSK");
- NETWORK_3_VARS.put(KEY_PRIORITY, "6");
- NETWORK_3_VARS.put(KEY_DISABLED, "1");
- NETWORK_3_VARS.put(KEY_ID_STR, ""
- + "\"%7B%22creatorUid%22%3A%221000%22%2C%22configKey%22%3A%22%5C%22"
- + "testwpa2psk%5C%22WPA_PSK%22%7D\"");
- NETWORK_3_VARS.put(CONFIG_KEY, "\"testwpa2psk\"WPA_PSK");
- }
-
- private static final ArrayList<HashMap<String, String>> NETWORK_VARS = new ArrayList<HashMap<String, String>>();
- static {
- NETWORK_VARS.add(NETWORK_0_VARS);
- NETWORK_VARS.add(NETWORK_1_VARS);
- NETWORK_VARS.add(NETWORK_2_VARS);
- NETWORK_VARS.add(NETWORK_3_VARS);
- }
-
- // Taken from wpa_supplicant.conf actual test device Some fields modified for privacy.
- private static final String TEST_WPA_SUPPLICANT_CONF = ""
- + "ctrl_interface=/data/misc/wifi/sockets\n"
- + "disable_scan_offload=1\n"
- + "driver_param=use_p2p_group_interface=1p2p_device=1\n"
- + "update_config=1\n"
- + "device_name=testdevice\n"
- + "manufacturer=TestManufacturer\n"
- + "model_name=Testxus\n"
- + "model_number=Testxus\n"
- + "serial_number=1ABCD12345678912\n"
- + "device_type=12-3456A456-7\n"
- + "config_methods=physical_display virtual_push_button\n"
- + "p2p_no_go_freq=5170-5740\n"
- + "pmf=1\n"
- + "external_sim=1\n"
- + "wowlan_triggers=any\n"
- + "p2p_search_delay=0\n"
- + "network={\n"
- + " " + KEY_SSID + "=" + NETWORK_0_VARS.get(KEY_SSID) + "\n"
- + " " + KEY_KEY_MGMT + "=" + NETWORK_0_VARS.get(KEY_KEY_MGMT) + "\n"
- + " " + KEY_PRIORITY + "=" + NETWORK_0_VARS.get(KEY_PRIORITY) + "\n"
- + " " + KEY_ID_STR + "=" + NETWORK_0_VARS.get(KEY_ID_STR) + "\n"
- + "}\n"
- + "\n"
- + "network={\n"
- + " " + KEY_SSID + "=" + NETWORK_1_VARS.get(KEY_SSID) + "\n"
- + " " + KEY_KEY_MGMT + "=" + NETWORK_1_VARS.get(KEY_KEY_MGMT) + "\n"
- + " " + KEY_PRIORITY + "=" + NETWORK_1_VARS.get(KEY_PRIORITY) + "\n"
- + " " + KEY_DISABLED + "=" + NETWORK_1_VARS.get(KEY_DISABLED) + "\n"
- + " " + KEY_ID_STR + "=" + NETWORK_1_VARS.get(KEY_ID_STR) + "\n"
- + "}\n"
- + "\n"
- + "network={\n"
- + " " + KEY_SSID + "=" + NETWORK_2_VARS.get(KEY_SSID) + "\n"
- + " " + KEY_KEY_MGMT + "=" + NETWORK_2_VARS.get(KEY_KEY_MGMT) + "\n"
- + " " + KEY_PRIORITY + "=" + NETWORK_2_VARS.get(KEY_PRIORITY) + "\n"
- + " " + KEY_DISABLED + "=" + NETWORK_2_VARS.get(KEY_DISABLED) + "\n"
- + " " + KEY_ID_STR + "=" + NETWORK_2_VARS.get(KEY_ID_STR) + "\n"
- + "}\n"
- + "\n"
- + "network={\n"
- + " " + KEY_SSID + "=" + NETWORK_3_VARS.get(KEY_SSID) + "\n"
- + " " + KEY_PSK + "=" + NETWORK_3_VARS.get(KEY_PSK) + "\n"
- + " " + KEY_KEY_MGMT + "=" + NETWORK_3_VARS.get(KEY_KEY_MGMT) + "\n"
- + " " + KEY_PRIORITY + "=" + NETWORK_3_VARS.get(KEY_PRIORITY) + "\n"
- + " " + KEY_DISABLED + "=" + NETWORK_3_VARS.get(KEY_DISABLED) + "\n"
- + " " + KEY_ID_STR + "=" + NETWORK_3_VARS.get(KEY_ID_STR) + "\n"
- + "}\n";
-
- @Mock private WifiNative mWifiNative;
+ // Test mocks
@Mock private Context mContext;
- private MockKeyStore mMockKeyStore;
+ private TestAlarmManager mAlarmManager;
+ private TestLooper mLooper;
+ @Mock private Clock mClock;
+ private MockStoreFile mSharedStore;
+ private MockStoreFile mUserStore;
+ private MockStoreData mStoreData;
+
+ /**
+ * Test instance of WifiConfigStore.
+ */
private WifiConfigStore 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();
+ mStoreData = new MockStoreData();
+ }
+
+ /**
+ * Setup the test environment.
+ */
@Before
public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
+ setupMocks();
- mMockKeyStore = new MockKeyStore();
- mWifiConfigStore = new WifiConfigStore(mContext, mWifiNative, mMockKeyStore.createMock(),
- null, false, true);
+ mWifiConfigStore = new WifiConfigStore(mContext, mLooper.getLooper(), mClock, mSharedStore);
+ // Enable verbose logging before tests.
+ mWifiConfigStore.enableVerboseLogging(true);
}
/**
- * Verifies that readNetworkVariableFromSupplicantFile() properly reads network variables from a
- * wpa_supplicant.conf file.
+ * Called after each test
+ */
+ @After
+ public void cleanup() {
+ validateMockitoUsage();
+ }
+
+ /**
+ * Verify the contents of the config file with empty data. The data content should be the
+ * same as {@link #EMPTY_FILE_CONTENT}.
+ *
+ * @throws Exception
*/
@Test
- public void readNetworkVariableFromSupplicantFile() throws Exception {
- Map<String, String> ssidResults = readNetworkVariableFromSupplicantFile(KEY_SSID);
- assertEquals(NETWORK_VARS.size(), ssidResults.size());
- for (HashMap<String, String> single_network_vars : NETWORK_VARS) {
- assertEquals(ssidResults.get(single_network_vars.get(CONFIG_KEY)),
- single_network_vars.get(KEY_SSID));
+ public void testWriteWithEmptyData() throws Exception {
+ // Perform force write to both share and user store file.
+ mWifiConfigStore.switchUserStoreAndRead(mUserStore);
+ mWifiConfigStore.write(true);
+
+ assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
+ assertTrue(mSharedStore.isStoreWritten());
+ assertTrue(mUserStore.isStoreWritten());
+ assertTrue(Arrays.equals(EMPTY_FILE_CONTENT.getBytes(StandardCharsets.UTF_8),
+ mSharedStore.getStoreBytes()));
+ assertTrue(Arrays.equals(EMPTY_FILE_CONTENT.getBytes(StandardCharsets.UTF_8),
+ mUserStore.getStoreBytes()));
+ }
+
+ /**
+ * 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.switchUserStoreAndRead(mUserStore);
+ mWifiConfigStore.write(true);
+
+ assertFalse(mAlarmManager.isPending(WifiConfigStore.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.switchUserStoreAndRead(mUserStore);
+ mWifiConfigStore.write(false);
+
+ assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
+ assertFalse(mSharedStore.isStoreWritten());
+ assertFalse(mUserStore.isStoreWritten());
+
+ // Now send the alarm and ensure that the writes happen.
+ mAlarmManager.dispatch(WifiConfigStore.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 {
+ // Register a test data container with bogus data.
+ mWifiConfigStore.registerStoreData(mStoreData);
+ mStoreData.setShareData("abcds");
+ mStoreData.setUserData("asdfa");
+
+ // Perform buffered write for both user and share store file.
+ mWifiConfigStore.switchUserStoreAndRead(mUserStore);
+ mWifiConfigStore.write(false);
+
+ assertTrue(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
+ assertFalse(mSharedStore.isStoreWritten());
+ assertFalse(mUserStore.isStoreWritten());
+
+ // Update the container with new set of data. The send a force write and ensure that the
+ // writes have been performed and alarms have been stopped and updated data are written.
+ mStoreData.setUserData(TEST_USER_DATA);
+ mStoreData.setShareData(TEST_SHARE_DATA);
+ mWifiConfigStore.write(true);
+
+ assertFalse(mAlarmManager.isPending(WifiConfigStore.BUFFERED_WRITE_ALARM_TAG));
+ assertTrue(mSharedStore.isStoreWritten());
+ assertTrue(mUserStore.isStoreWritten());
+
+ // Verify correct data are loaded to the data container after a read.
+ mWifiConfigStore.read();
+ assertEquals(TEST_USER_DATA, mStoreData.getUserData());
+ assertEquals(TEST_SHARE_DATA, mStoreData.getShareData());
+ }
+
+ /**
+ * Tests the read API behaviour after a write to the store files.
+ * Expected behaviour: The read should return the same data that was last written.
+ */
+ @Test
+ public void testReadAfterWrite() throws Exception {
+ // Register data container.
+ mWifiConfigStore.registerStoreData(mStoreData);
+
+ // Read both share and user config store.
+ mWifiConfigStore.switchUserStoreAndRead(mUserStore);
+
+ // Verify no data is read.
+ assertNull(mStoreData.getUserData());
+ assertNull(mStoreData.getShareData());
+
+ // Write share and user data.
+ mStoreData.setUserData(TEST_USER_DATA);
+ mStoreData.setShareData(TEST_SHARE_DATA);
+ mWifiConfigStore.write(true);
+
+ // Read and verify the data content in the data container.
+ mWifiConfigStore.read();
+ assertEquals(TEST_USER_DATA, mStoreData.getUserData());
+ assertEquals(TEST_SHARE_DATA, mStoreData.getShareData());
+ }
+
+ /**
+ * Tests the read API behaviour when there is no store files 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.
+ mWifiConfigStore.registerStoreData(mStoreData);
+ assertFalse(mWifiConfigStore.areStoresPresent());
+ mWifiConfigStore.read();
+
+ // Empty data.
+ assertNull(mStoreData.getUserData());
+ assertNull(mStoreData.getShareData());
+ }
+
+ /**
+ * Tests the read API behaviour after a write to the shared store file when the user
+ * store file is null.
+ * Expected behaviour: The read should return the same data that was last written.
+ */
+ @Test
+ public void testReadAfterWriteWithNoUserStore() throws Exception {
+ // Setup data container.
+ mWifiConfigStore.registerStoreData(mStoreData);
+ mStoreData.setUserData(TEST_USER_DATA);
+ mStoreData.setShareData(TEST_SHARE_DATA);
+
+ // Perform write for the share store file.
+ mWifiConfigStore.write(true);
+ mWifiConfigStore.read();
+ // Verify data content for both user and share data.
+ assertEquals(TEST_SHARE_DATA, mStoreData.getShareData());
+ assertNull(mStoreData.getUserData());
+ }
+
+ /**
+ * Verifies that a read operation will reset the data in the data container, to avoid
+ * any stale data from previous read.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testReadWillResetStoreData() throws Exception {
+ // Register and setup store data.
+ mWifiConfigStore.registerStoreData(mStoreData);
+
+ // Perform force write with empty data content to both user and share store file.
+ mWifiConfigStore.switchUserStoreAndRead(mUserStore);
+ mWifiConfigStore.write(true);
+
+ // Setup data container with some value.
+ mStoreData.setUserData(TEST_USER_DATA);
+ mStoreData.setShareData(TEST_SHARE_DATA);
+
+ // Perform read of both user and share store file and verify data in the data container
+ // is in sync (empty) with what is in the file.
+ mWifiConfigStore.read();
+ assertNull(mStoreData.getShareData());
+ assertNull(mStoreData.getUserData());
+ }
+
+ /**
+ * Verify that a store file contained WiFi configuration store data (network list and
+ * deleted ephemeral SSID list) using the predefined test XML data is read and parsed
+ * correctly.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testReadWifiConfigStoreData() throws Exception {
+ // Setup network list.
+ NetworkListStoreData networkList = new NetworkListStoreData();
+ mWifiConfigStore.registerStoreData(networkList);
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ openNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
+ List<WifiConfiguration> userConfigs = new ArrayList<>();
+ userConfigs.add(openNetwork);
+
+ // Setup deleted ephemeral SSID list.
+ DeletedEphemeralSsidsStoreData deletedEphemeralSsids =
+ new DeletedEphemeralSsidsStoreData();
+ mWifiConfigStore.registerStoreData(deletedEphemeralSsids);
+ String testSsid = "Test SSID";
+ Set<String> ssidList = new HashSet<>();
+ ssidList.add(testSsid);
+
+ // Setup user store XML bytes.
+ String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
+ openNetwork.configKey().replaceAll("\"", """),
+ openNetwork.SSID.replaceAll("\"", """),
+ openNetwork.shared, openNetwork.creatorUid, testSsid);
+ byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8);
+ mUserStore.storeRawDataToWrite(xmlBytes);
+
+ mWifiConfigStore.switchUserStoreAndRead(mUserStore);
+ WifiConfigurationTestUtil.assertConfigurationsEqualForConfigStore(
+ userConfigs, networkList.getUserConfigurations());
+ assertEquals(ssidList, deletedEphemeralSsids.getSsidList());
+ }
+
+ /**
+ * Verify that the WiFi configuration store data containing network list and deleted
+ * ephemeral SSID list are serialized correctly, matches the predefined test XML data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testWriteWifiConfigStoreData() throws Exception {
+ // Setup user store.
+ mWifiConfigStore.switchUserStoreAndRead(mUserStore);
+
+ // Setup network list store data.
+ NetworkListStoreData networkList = new NetworkListStoreData();
+ mWifiConfigStore.registerStoreData(networkList);
+ WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork();
+ openNetwork.setIpConfiguration(
+ WifiConfigurationTestUtil.createDHCPIpConfigurationWithNoProxy());
+ List<WifiConfiguration> userConfigs = new ArrayList<>();
+ userConfigs.add(openNetwork);
+ networkList.setUserConfigurations(userConfigs);
+
+ // Setup deleted ephemeral SSID list store data.
+ DeletedEphemeralSsidsStoreData deletedEphemeralSsids =
+ new DeletedEphemeralSsidsStoreData();
+ mWifiConfigStore.registerStoreData(deletedEphemeralSsids);
+ String testSsid = "Test SSID";
+ Set<String> ssidList = new HashSet<>();
+ ssidList.add(testSsid);
+ deletedEphemeralSsids.setSsidList(ssidList);
+
+ // Setup expected XML bytes.
+ String xmlString = String.format(TEST_DATA_XML_STRING_FORMAT,
+ openNetwork.configKey().replaceAll("\"", """),
+ openNetwork.SSID.replaceAll("\"", """),
+ openNetwork.shared, openNetwork.creatorUid, testSsid);
+ byte[] xmlBytes = xmlString.getBytes(StandardCharsets.UTF_8);
+
+ mWifiConfigStore.write(true);
+ assertEquals(xmlBytes.length, mUserStore.getStoreBytes().length);
+ // Verify the user store content.
+ assertTrue(Arrays.equals(xmlBytes, mUserStore.getStoreBytes()));
+ }
+
+ /**
+ * Verify that a XmlPullParserException will be thrown when reading an user store file
+ * containing unknown data.
+ *
+ * @throws Exception
+ */
+ @Test(expected = XmlPullParserException.class)
+ public void testReadUserStoreContainedUnknownData() throws Exception {
+ String storeFileData =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"1\" />\n"
+ + "<UnknownTag>\n" // No StoreData registered to handle this tag.
+ + "</UnknownTag>\n"
+ + "</WifiConfigStoreData>\n";
+ mUserStore.storeRawDataToWrite(storeFileData.getBytes(StandardCharsets.UTF_8));
+ mWifiConfigStore.switchUserStoreAndRead(mUserStore);
+ }
+
+ /**
+ * Verify that a XmlPullParserException will be thrown when reading the share store file
+ * containing unknown data.
+ *
+ * @throws Exception
+ */
+ @Test(expected = XmlPullParserException.class)
+ public void testReadShareStoreContainedUnknownData() throws Exception {
+ String storeFileData =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<WifiConfigStoreData>\n"
+ + "<int name=\"Version\" value=\"1\" />\n"
+ + "<UnknownTag>\n" // No StoreData registered to handle this tag.
+ + "</UnknownTag>\n"
+ + "</WifiConfigStoreData>\n";
+ mSharedStore.storeRawDataToWrite(storeFileData.getBytes(StandardCharsets.UTF_8));
+ mWifiConfigStore.read();
+ }
+
+ /**
+ * Mock Store File to redirect all file writes from WifiConfigStore to local buffers.
+ * This can be used to examine the data output by WifiConfigStore.
+ */
+ private class MockStoreFile extends StoreFile {
+ private byte[] mStoreBytes;
+ private boolean mStoreWritten;
+
+ public MockStoreFile() {
+ super(new File("MockStoreFile"));
}
- Map<String, String> pskResults = readNetworkVariableFromSupplicantFile(KEY_PSK);
- // Only network 3 is secured with a password.
- assertEquals(1, pskResults.size());
- assertEquals(pskResults.get(NETWORK_3_VARS.get(CONFIG_KEY)),
- NETWORK_3_VARS.get(KEY_PSK));
-
- Map<String, String> keyMgmtResults = readNetworkVariableFromSupplicantFile(KEY_KEY_MGMT);
- assertEquals(NETWORK_VARS.size(), keyMgmtResults.size());
- for (HashMap<String, String> single_network_vars : NETWORK_VARS) {
- assertEquals(keyMgmtResults.get(single_network_vars.get(CONFIG_KEY)),
- single_network_vars.get(KEY_KEY_MGMT));
+ @Override
+ public byte[] readRawData() {
+ return mStoreBytes;
}
- Map<String, String> priorityResults = readNetworkVariableFromSupplicantFile(KEY_PRIORITY);
- assertEquals(NETWORK_VARS.size(), priorityResults.size());
- for (HashMap<String, String> single_network_vars : NETWORK_VARS) {
- assertEquals(priorityResults.get(single_network_vars.get(CONFIG_KEY)),
- single_network_vars.get(KEY_PRIORITY));
+ @Override
+ public void storeRawDataToWrite(byte[] data) {
+ mStoreBytes = data;
+ mStoreWritten = false;
}
- Map<String, String> disabledResults = readNetworkVariableFromSupplicantFile(KEY_DISABLED);
- // All but network 0 are disabled.
- assertEquals(NETWORK_VARS.size() - 1, disabledResults.size());
- for (int i = 1; i < NETWORK_VARS.size(); ++i) {
- assertEquals(disabledResults.get(NETWORK_VARS.get(i).get(CONFIG_KEY)),
- NETWORK_VARS.get(i).get(KEY_DISABLED));
+ @Override
+ public boolean exists() {
+ return (mStoreBytes != null);
}
- Map<String, String> idStrResults = readNetworkVariableFromSupplicantFile(KEY_ID_STR);
- assertEquals(NETWORK_VARS.size(), idStrResults.size());
- for (HashMap<String, String> single_network_vars : NETWORK_VARS) {
- assertEquals(idStrResults.get(single_network_vars.get(CONFIG_KEY)),
- single_network_vars.get(KEY_ID_STR));
+ @Override
+ public void writeBufferedRawData() {
+ mStoreWritten = true;
+ }
+
+ public byte[] getStoreBytes() {
+ return mStoreBytes;
+ }
+
+ public boolean isStoreWritten() {
+ return mStoreWritten;
}
}
/**
- * Inject |TEST_WPA_SUPPLICANT_CONF| via the helper method readNetworkVariablesFromReader().
+ * Mock data container for providing test data for the store file.
*/
- public Map<String, String> readNetworkVariableFromSupplicantFile(String key) throws Exception {
- Map<String, String> result = new HashMap<>();
- BufferedReader reader = null;
- try {
- reader = new BufferedReader(new StringReader(TEST_WPA_SUPPLICANT_CONF));
- result = mWifiConfigStore.readNetworkVariablesFromReader(reader, key);
- } catch (IOException e) {
- fail("Error reading test supplicant conf string");
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (IOException e) {
- // Just ignore if we can't close the reader.
+ private class MockStoreData implements WifiConfigStore.StoreData {
+ private static final String XML_TAG_TEST_HEADER = "TestHeader";
+ private static final String XML_TAG_TEST_DATA = "TestData";
+
+ private String mShareData;
+ private String mUserData;
+
+ MockStoreData() {}
+
+ @Override
+ public void serializeData(XmlSerializer out, boolean shared)
+ throws XmlPullParserException, IOException {
+ if (shared) {
+ XmlUtil.writeNextValue(out, XML_TAG_TEST_DATA, mShareData);
+ } else {
+ XmlUtil.writeNextValue(out, XML_TAG_TEST_DATA, mUserData);
}
}
- return result;
+
+ @Override
+ public void deserializeData(XmlPullParser in, int outerTagDepth, boolean shared)
+ throws XmlPullParserException, IOException {
+ if (shared) {
+ mShareData = (String) XmlUtil.readNextValueWithName(in, XML_TAG_TEST_DATA);
+ } else {
+ mUserData = (String) XmlUtil.readNextValueWithName(in, XML_TAG_TEST_DATA);
+ }
+ }
+
+ @Override
+ public void resetData(boolean shared) {
+ if (shared) {
+ mShareData = null;
+ } else {
+ mUserData = null;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return XML_TAG_TEST_HEADER;
+ }
+
+ @Override
+ public boolean supportShareData() {
+ return true;
+ }
+
+ public String getShareData() {
+ return mShareData;
+ }
+
+ public void setShareData(String shareData) {
+ mShareData = shareData;
+ }
+
+ public String getUserData() {
+ return mUserData;
+ }
+
+ public void setUserData(String userData) {
+ mUserData = userData;
+ }
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationTestUtil.java
index 7117c2a..f7bf5b0 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.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.Arrays;
+import java.util.List;
/**
* Helper for creating and populating WifiConfigurations in unit tests.
@@ -33,6 +47,47 @@
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 = 5;
+ public static final String TEST_SSID = "WifiConfigurationTestUtilSSID";
+ public static final String TEST_PSK = "\"WifiConfigurationTestUtilPsk\"";
+ public static final String[] TEST_WEP_KEYS =
+ {"\"WifiConfigurationTestUtilWep1\"", "\"WifiConfigurationTestUtilWep2\"",
+ "45342312ab", "45342312ab45342312ab34ac12"};
+ public static final String TEST_EAP_PASSWORD = "WifiConfigurationTestUtilEapPassword";
+ 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";
+
+ private static final int MAX_SSID_LENGTH = 32;
+ /**
+ * 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
@@ -52,11 +107,8 @@
config.shared = shared;
config.status = enabled ? WifiConfiguration.Status.ENABLED
: WifiConfiguration.Status.DISABLED;
- if (fqdn != null) {
- config.FQDN = fqdn;
- config.providerFriendlyName = providerFriendlyName;
- config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM);
- }
+ config.FQDN = fqdn;
+ config.providerFriendlyName = providerFriendlyName;
return config;
}
@@ -78,18 +130,544 @@
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);
}
if ((security & SECURITY_EAP) != 0) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+ config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
}
}
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() {
+ String ssid = TEST_SSID + sNetworkIndex++;
+ assertTrue(ssid.length() <= MAX_SSID_LENGTH);
+ return "\"" + ssid + "\"";
+ }
+
+ /**
+ * 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 createPskNetwork(String ssid) {
+ WifiConfiguration configuration =
+ generateWifiConfig(TEST_NETWORK_ID, TEST_UID, ssid, 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,
+ null, null, SECURITY_EAP);
+ return configuration;
+ }
+
+ public static WifiConfiguration createEapNetwork(String ssid) {
+ WifiConfiguration configuration =
+ generateWifiConfig(TEST_NETWORK_ID, TEST_UID, ssid, true, true,
+ null, null, SECURITY_EAP);
+ return configuration;
+ }
+
+
+ public static WifiConfiguration createEapNetwork(int eapMethod, int phase2Method) {
+ WifiConfiguration configuration =
+ generateWifiConfig(TEST_NETWORK_ID, TEST_UID, createNewSSID(), true, true,
+ null, null, SECURITY_EAP);
+ configuration.enterpriseConfig.setEapMethod(eapMethod);
+ configuration.enterpriseConfig.setPhase2Method(phase2Method);
+ return configuration;
+ }
+
+ public static WifiConfiguration createPasspointNetwork() {
+ 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);
+ }
+
+ /**
+ * Creates an IP configuration with specific parameters.
+ * @param proxySetting Must be one of {@link WifiConfigurationTestUtil#STATIC_PROXY_SETTING},
+ * {@link WifiConfigurationTestUtil#PAC_PROXY_SETTING},
+ * {@link WifiConfigurationTestUtil#NONE_PROXY_SETTING}
+ */
+ public static IpConfiguration createDHCPIpConfigurationWithSpecificProxy(
+ int proxySetting,
+ String staticProxyHost,
+ int staticProxyPort,
+ String staticProxyExclusionList,
+ String pacProxyLocation) {
+ return generateIpConfig(
+ DHCP_IP_ASSIGNMENT, proxySetting,
+ TEST_STATIC_IP_LINK_ADDRESS, TEST_STATIC_IP_LINK_PREFIX_LENGTH,
+ TEST_STATIC_IP_GATEWAY_ADDRESS, TEST_STATIC_IP_DNS_SERVER_ADDRESSES,
+ staticProxyHost, staticProxyPort, staticProxyExclusionList,
+ pacProxyLocation);
+ }
+
+ // 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;
+ }
+
+ public static WifiEnterpriseConfig createTLSWifiEnterpriseConfigWithAkaPhase2() {
+ WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+ config.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ config.setPhase2Method(WifiEnterpriseConfig.Phase2.AKA);
+ 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) {
+ assertNotNull(expected);
+ assertNotNull(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.requirePMF, actual.requirePMF);
+ assertEquals(expected.allowedKeyManagement, actual.allowedKeyManagement);
+ assertEquals(expected.allowedProtocols, actual.allowedProtocols);
+ assertEquals(expected.allowedAuthAlgorithms, actual.allowedAuthAlgorithms);
+ assertEquals(expected.allowedGroupCiphers, actual.allowedGroupCiphers);
+ assertEquals(expected.allowedPairwiseCiphers, actual.allowedPairwiseCiphers);
+ 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.status, actual.status);
+ assertEquals(expected.FQDN, actual.FQDN);
+ assertEquals(expected.providerFriendlyName, actual.providerFriendlyName);
+ assertTrue(Arrays.equals(expected.roamingConsortiumIds, actual.roamingConsortiumIds));
+ 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);
+ assertEquals(expected.isLegacyPasspointConfig, actual.isLegacyPasspointConfig);
+ 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);
+ }
+
+ /**
+ * Asserts that the 2 WifiConfigurations are equal. This compares all the elements that are
+ * saved into wpa_supplicant by SupplicantStaNetwork.
+ */
+ public static void assertConfigurationEqualForSupplicant(
+ WifiConfiguration expected, WifiConfiguration actual) {
+ assertNotNull(expected);
+ assertNotNull(actual);
+ assertEquals(expected.SSID, actual.SSID);
+ assertEquals(expected.getNetworkSelectionStatus().getNetworkSelectionBSSID(),
+ actual.getNetworkSelectionStatus().getNetworkSelectionBSSID());
+ assertEquals(expected.preSharedKey, actual.preSharedKey);
+ assertEquals(expected.wepKeys, actual.wepKeys);
+ assertEquals(expected.wepTxKeyIndex, actual.wepTxKeyIndex);
+ assertEquals(expected.hiddenSSID, actual.hiddenSSID);
+ assertEquals(expected.requirePMF, actual.requirePMF);
+ assertEquals(expected.allowedKeyManagement, actual.allowedKeyManagement);
+ assertEquals(expected.allowedProtocols, actual.allowedProtocols);
+ assertEquals(expected.allowedAuthAlgorithms, actual.allowedAuthAlgorithms);
+ assertEquals(expected.allowedGroupCiphers, actual.allowedGroupCiphers);
+ assertEquals(expected.allowedPairwiseCiphers, actual.allowedPairwiseCiphers);
+ assertWifiEnterpriseConfigEqualForConfigStore(
+ expected.enterpriseConfig, actual.enterpriseConfig);
+ }
+
+ /**
+ * Asserts that the 2 WifiConfigurations are equal. This is a generic version of the comparator
+ * which is used in QNS tests for comparing the network selections.
+ * This importantly checks that the networkId's of the 2 configs are equal.
+ */
+ public static void assertConfigurationEqual(
+ WifiConfiguration expected, WifiConfiguration actual) {
+ assertCommonConfigurationElementsEqual(expected, actual);
+ assertEquals(expected.networkId, actual.networkId);
+ }
+
+ /**
+ * 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());
+ assertEquals(
+ NetworkSelectionStatus.NETWORK_SELECTION_ENABLE,
+ actual.getNetworkSelectionDisableReason());
+ } 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.getFieldValue(WifiEnterpriseConfig.REALM_KEY),
+ actual.getFieldValue(WifiEnterpriseConfig.REALM_KEY));
+ assertEquals(expected.getFieldValue(WifiEnterpriseConfig.PLMN_KEY),
+ actual.getFieldValue(WifiEnterpriseConfig.PLMN_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/WifiConfigurationUtilTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
index c5b5030..506db91 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java
@@ -16,17 +16,21 @@
package com.android.server.wifi;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
import android.content.pm.UserInfo;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiScanner;
import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Test;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
/**
@@ -37,6 +41,8 @@
static final int CURRENT_USER_ID = 0;
static final int CURRENT_USER_MANAGED_PROFILE_USER_ID = 10;
static final int OTHER_USER_ID = 11;
+ static final String TEST_SSID = "test_ssid";
+ static final String TEST_SSID_1 = "test_ssid_1";
static final List<UserInfo> PROFILES = Arrays.asList(
new UserInfo(CURRENT_USER_ID, "owner", 0),
new UserInfo(CURRENT_USER_MANAGED_PROFILE_USER_ID, "managed profile", 0));
@@ -63,4 +69,320 @@
configuration.creatorUid = UserHandle.getUid(CURRENT_USER_MANAGED_PROFILE_USER_ID, 0);
assertTrue(WifiConfigurationUtil.isVisibleToAnyProfile(configuration, PROFILES));
}
+
+ /**
+ * Verify that new WifiEnterpriseConfig is detected.
+ */
+ @Test
+ public void testEnterpriseConfigAdded() {
+ EnterpriseConfig eapConfig = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[]{FakeKeys.CA_CERT0});
+
+ assertTrue(WifiConfigurationUtil.hasEnterpriseConfigChanged(
+ null, eapConfig.enterpriseConfig));
+ }
+
+ /**
+ * Verify WifiEnterpriseConfig eap change is detected.
+ */
+ @Test
+ public void testEnterpriseConfigEapChangeDetected() {
+ EnterpriseConfig eapConfig = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS);
+ EnterpriseConfig peapConfig = new EnterpriseConfig(WifiEnterpriseConfig.Eap.PEAP);
+
+ assertTrue(WifiConfigurationUtil.hasEnterpriseConfigChanged(eapConfig.enterpriseConfig,
+ peapConfig.enterpriseConfig));
+ }
+
+ /**
+ * Verify WifiEnterpriseConfig phase2 method change is detected.
+ */
+ @Test
+ public void testEnterpriseConfigPhase2ChangeDetected() {
+ EnterpriseConfig eapConfig =
+ new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2);
+ EnterpriseConfig papConfig =
+ new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.PAP);
+
+ assertTrue(WifiConfigurationUtil.hasEnterpriseConfigChanged(eapConfig.enterpriseConfig,
+ papConfig.enterpriseConfig));
+ }
+
+ /**
+ * Verify WifiEnterpriseConfig added Certificate is detected.
+ */
+ @Test
+ public void testCaCertificateAddedDetected() {
+ EnterpriseConfig eapConfigNoCerts = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2)
+ .setIdentity("username", "password");
+
+ EnterpriseConfig eapConfig1Cert = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[]{FakeKeys.CA_CERT0});
+
+ assertTrue(WifiConfigurationUtil.hasEnterpriseConfigChanged(
+ eapConfigNoCerts.enterpriseConfig, eapConfig1Cert.enterpriseConfig));
+ }
+
+ /**
+ * Verify WifiEnterpriseConfig Certificate change is detected.
+ */
+ @Test
+ public void testDifferentCaCertificateDetected() {
+ EnterpriseConfig eapConfig = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[]{FakeKeys.CA_CERT0});
+
+ EnterpriseConfig eapConfigNewCert = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[]{FakeKeys.CA_CERT1});
+
+ assertTrue(WifiConfigurationUtil.hasEnterpriseConfigChanged(eapConfig.enterpriseConfig,
+ eapConfigNewCert.enterpriseConfig));
+ }
+
+ /**
+ * Verify WifiEnterpriseConfig added Certificate changes are detected.
+ */
+ @Test
+ public void testCaCertificateChangesDetected() {
+ EnterpriseConfig eapConfig = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[]{FakeKeys.CA_CERT0});
+
+ EnterpriseConfig eapConfigAddedCert = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[]{FakeKeys.CA_CERT0, FakeKeys.CA_CERT1});
+
+ assertTrue(WifiConfigurationUtil.hasEnterpriseConfigChanged(eapConfig.enterpriseConfig,
+ eapConfigAddedCert.enterpriseConfig));
+ }
+
+ /**
+ * Verify that WifiEnterpriseConfig does not detect changes for identical configs.
+ */
+ @Test
+ public void testWifiEnterpriseConfigNoChanges() {
+ EnterpriseConfig eapConfig = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[]{FakeKeys.CA_CERT0, FakeKeys.CA_CERT1});
+
+ // Just to be clear that check is not against the same object
+ EnterpriseConfig eapConfigSame = new EnterpriseConfig(WifiEnterpriseConfig.Eap.TTLS)
+ .setPhase2(WifiEnterpriseConfig.Phase2.MSCHAPV2)
+ .setIdentity("username", "password")
+ .setCaCerts(new X509Certificate[]{FakeKeys.CA_CERT0, FakeKeys.CA_CERT1});
+
+ assertFalse(WifiConfigurationUtil.hasEnterpriseConfigChanged(eapConfig.enterpriseConfig,
+ eapConfigSame.enterpriseConfig));
+ }
+
+ /**
+ * Verify the instance of {@link android.net.wifi.WifiScanner.PnoSettings.PnoNetwork} created
+ * for an open network using {@link WifiConfigurationUtil#createPnoNetwork(
+ * WifiConfiguration, int)}.
+ */
+ @Test
+ public void testCreatePnoNetworkWithOpenNetwork() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createOpenNetwork();
+ WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
+ WifiConfigurationUtil.createPnoNetwork(network, 1);
+ assertEquals(network.SSID, pnoNetwork.ssid);
+ assertEquals(
+ WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND
+ | WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND, pnoNetwork.flags);
+ assertEquals(WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN, pnoNetwork.authBitField);
+ }
+
+ /**
+ * Verify the instance of {@link android.net.wifi.WifiScanner.PnoSettings.PnoNetwork} created
+ * for an open hidden network using {@link WifiConfigurationUtil#createPnoNetwork(
+ * WifiConfiguration, int)}.
+ */
+ @Test
+ public void testCreatePnoNetworkWithOpenHiddenNetwork() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createOpenHiddenNetwork();
+ WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
+ WifiConfigurationUtil.createPnoNetwork(network, 1);
+ assertEquals(network.SSID, pnoNetwork.ssid);
+ assertEquals(
+ WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND
+ | WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND
+ | WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN, pnoNetwork.flags);
+ assertEquals(WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_OPEN, pnoNetwork.authBitField);
+ }
+
+ /**
+ * Verify the instance of {@link android.net.wifi.WifiScanner.PnoSettings.PnoNetwork} created
+ * for a PSK network using {@link WifiConfigurationUtil#createPnoNetwork(WifiConfiguration, int)
+ * }.
+ */
+ @Test
+ public void testCreatePnoNetworkWithPskNetwork() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork();
+ WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
+ WifiConfigurationUtil.createPnoNetwork(network, 1);
+ assertEquals(network.SSID, pnoNetwork.ssid);
+ assertEquals(
+ WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND
+ | WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND, pnoNetwork.flags);
+ assertEquals(WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_PSK, pnoNetwork.authBitField);
+ }
+
+ /**
+ * Verify that WifiConfigurationUtil.isSameNetwork returns true when two WifiConfiguration
+ * objects have the same parameters.
+ */
+ @Test
+ public void testIsSameNetworkReturnsTrueOnSameNetwork() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID);
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID);
+ assertTrue(WifiConfigurationUtil.isSameNetwork(network, network1));
+ }
+
+ /**
+ * Verify that WifiConfigurationUtil.isSameNetwork returns false when two WifiConfiguration
+ * objects have the different SSIDs.
+ */
+ @Test
+ public void testIsSameNetworkReturnsFalseOnDifferentSSID() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID);
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID_1);
+ assertFalse(WifiConfigurationUtil.isSameNetwork(network, network1));
+ }
+
+ /**
+ * Verify that WifiConfigurationUtil.isSameNetwork returns false when two WifiConfiguration
+ * objects have the different security type.
+ */
+ @Test
+ public void testIsSameNetworkReturnsFalseOnDifferentSecurityType() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID);
+ WifiConfiguration network1 = WifiConfigurationTestUtil.createEapNetwork(TEST_SSID);
+ assertFalse(WifiConfigurationUtil.isSameNetwork(network, network1));
+ }
+
+
+ /**
+ * Verify the instance of {@link android.net.wifi.WifiScanner.PnoSettings.PnoNetwork} created
+ * for a EAP network using {@link WifiConfigurationUtil#createPnoNetwork(WifiConfiguration, int)
+ * }.
+ */
+ @Test
+ public void testCreatePnoNetworkWithEapNetwork() {
+ WifiConfiguration network = WifiConfigurationTestUtil.createEapNetwork();
+ WifiScanner.PnoSettings.PnoNetwork pnoNetwork =
+ WifiConfigurationUtil.createPnoNetwork(network, 1);
+ assertEquals(network.SSID, pnoNetwork.ssid);
+ assertEquals(
+ WifiScanner.PnoSettings.PnoNetwork.FLAG_A_BAND
+ | WifiScanner.PnoSettings.PnoNetwork.FLAG_G_BAND, pnoNetwork.flags);
+ assertEquals(WifiScanner.PnoSettings.PnoNetwork.AUTH_CODE_EAPOL, pnoNetwork.authBitField);
+ }
+
+ /**
+ * Verify that the generalized
+ * {@link com.android.server.wifi.WifiConfigurationUtil.WifiConfigurationComparator}
+ * can be used to sort a List given a 'compareNetworkWithSameStatus' method.
+ */
+ @Test
+ public void testPnoListComparator() {
+ List<WifiConfiguration> networks = new ArrayList<>();
+ final WifiConfiguration enabledNetwork1 = WifiConfigurationTestUtil.createEapNetwork();
+ enabledNetwork1.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
+ final WifiConfiguration enabledNetwork2 = WifiConfigurationTestUtil.createEapNetwork();
+ enabledNetwork2.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
+ final WifiConfiguration tempDisabledNetwork1 = WifiConfigurationTestUtil.createEapNetwork();
+ tempDisabledNetwork1.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
+ final WifiConfiguration tempDisabledNetwork2 = WifiConfigurationTestUtil.createEapNetwork();
+ tempDisabledNetwork2.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
+ WifiConfiguration permDisabledNetwork = WifiConfigurationTestUtil.createEapNetwork();
+ permDisabledNetwork.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
+
+ // Add all the networks to the list.
+ networks.add(tempDisabledNetwork1);
+ networks.add(enabledNetwork1);
+ networks.add(permDisabledNetwork);
+ networks.add(tempDisabledNetwork2);
+ networks.add(enabledNetwork2);
+
+ // Prefer |enabledNetwork1| over |enabledNetwork2| and |tempDisabledNetwork1| over
+ // |tempDisabledNetwork2|.
+ WifiConfigurationUtil.WifiConfigurationComparator comparator =
+ new WifiConfigurationUtil.WifiConfigurationComparator() {
+ @Override
+ public int compareNetworksWithSameStatus(
+ WifiConfiguration a, WifiConfiguration b) {
+ if (a == enabledNetwork1 && b == enabledNetwork2) {
+ return -1;
+ } else if (b == enabledNetwork1 && a == enabledNetwork2) {
+ return 1;
+ } else if (a == tempDisabledNetwork1 && b == tempDisabledNetwork2) {
+ return -1;
+ } else if (b == tempDisabledNetwork1 && a == tempDisabledNetwork2) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+ Collections.sort(networks, comparator);
+
+ // Now ensure that the networks were sorted correctly.
+ assertEquals(enabledNetwork1, networks.get(0));
+ assertEquals(enabledNetwork2, networks.get(1));
+ assertEquals(tempDisabledNetwork1, networks.get(2));
+ assertEquals(tempDisabledNetwork2, networks.get(3));
+ assertEquals(permDisabledNetwork, networks.get(4));
+ }
+
+ private static class EnterpriseConfig {
+ public String eap;
+ public String phase2;
+ public String identity;
+ public String password;
+ public X509Certificate[] caCerts;
+ public WifiEnterpriseConfig enterpriseConfig;
+
+ EnterpriseConfig(int eapMethod) {
+ enterpriseConfig = new WifiEnterpriseConfig();
+ enterpriseConfig.setEapMethod(eapMethod);
+ eap = WifiEnterpriseConfig.Eap.strings[eapMethod];
+ }
+
+ public EnterpriseConfig setPhase2(int phase2Method) {
+ enterpriseConfig.setPhase2Method(phase2Method);
+ phase2 = "auth=" + WifiEnterpriseConfig.Phase2.strings[phase2Method];
+ return this;
+ }
+
+ public EnterpriseConfig setIdentity(String identity, String password) {
+ enterpriseConfig.setIdentity(identity);
+ enterpriseConfig.setPassword(password);
+ this.identity = identity;
+ this.password = password;
+ return this;
+ }
+
+ public EnterpriseConfig setCaCerts(X509Certificate[] certs) {
+ enterpriseConfig.setCaCertificates(certs);
+ caCerts = certs;
+ return this;
+ }
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java
new file mode 100644
index 0000000..4426ec1
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static android.net.wifi.WifiManager.WIFI_FEATURE_CONTROL_ROAMING;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiConnectivityHelper}.
+ */
+@SmallTest
+public class WifiConnectivityHelperTest {
+ /** Sets up test. */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ setupWifiNative();
+
+ mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative);
+ }
+
+ /** Cleans up test. */
+ @After
+ public void cleanup() {
+ validateMockitoUsage();
+ }
+
+ private WifiConnectivityHelper mWifiConnectivityHelper;
+ @Mock private WifiNative mWifiNative;
+ @Captor ArgumentCaptor<WifiNative.RoamingConfig> mRoamingConfigCaptor;
+ private int mFeatureSetValue;
+ private static final String TAG = "WifiConnectivityHelperTest";
+ private static final int MAX_BSSID_BLACKLIST_SIZE = 16;
+ private static final int MAX_SSID_WHITELIST_SIZE = 8;
+
+ private void setupWifiNative() {
+ // Return firmware roaming feature as supported by default.
+ when(mWifiNative.getSupportedFeatureSet()).thenReturn(WIFI_FEATURE_CONTROL_ROAMING);
+
+ doAnswer(new AnswerWithArguments() {
+ public boolean answer(WifiNative.RoamingCapabilities roamCap) throws Exception {
+ roamCap.maxBlacklistSize = MAX_BSSID_BLACKLIST_SIZE;
+ roamCap.maxWhitelistSize = MAX_SSID_WHITELIST_SIZE;
+ return true;
+ }}).when(mWifiNative).getRoamingCapabilities(anyObject());
+
+ when(mWifiNative.configureRoaming(anyObject())).thenReturn(true);
+ }
+
+ private ArrayList<String> buildBssidBlacklist(int size) {
+ ArrayList<String> bssidBlacklist = new ArrayList<String>();
+
+ for (int i = 0; i < size; i++) {
+ StringBuilder bssid = new StringBuilder("11:22:33:44:55:66");
+ bssid.setCharAt(16, (char) ('0' + i));
+ bssidBlacklist.add(bssid.toString());
+ }
+
+ return bssidBlacklist;
+ }
+
+ private ArrayList<String> buildSsidWhitelist(int size) {
+ ArrayList<String> ssidWhitelist = new ArrayList<String>();
+
+ for (int i = 0; i < size; i++) {
+ StringBuilder ssid = new StringBuilder("\"Test_Ap_0\"");
+ ssid.setCharAt(9, (char) ('0' + i));
+ ssidWhitelist.add(ssid.toString());
+ }
+
+ return ssidWhitelist;
+ }
+
+ /**
+ * When WifiNative has WIFI_FEATURE_CONTROL_ROAMING set, verify that
+ * WifiConnectivityHelper#isFirmwareRoamingSupported returns true.
+ */
+ @Test
+ public void returnFirmwareRoamingSupported() {
+ //By default WifiNative has WIFI_FEATURE_CONTROL_ROAMING set in its feature set.
+ assertTrue(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ assertTrue(mWifiConnectivityHelper.isFirmwareRoamingSupported());
+ }
+
+ /**
+ * When WifiNative doesn't have WIFI_FEATURE_CONTROL_ROAMING set, verify that
+ * WifiConnectivityHelper#isFirmwareRoamingSupported returns false.
+ */
+ @Test
+ public void returnFirmwareRoamingNotSupported() {
+ when(mWifiNative.getSupportedFeatureSet()).thenReturn(~WIFI_FEATURE_CONTROL_ROAMING);
+ assertTrue(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ assertFalse(mWifiConnectivityHelper.isFirmwareRoamingSupported());
+ }
+
+ /**
+ * Verify that correct firmware roaming capability values are returned if querying
+ * WifiNative for roaming capability succeeded.
+ */
+ @Test
+ public void verifyFirmwareRoamingCapabilityWithSuccessfulNativeCall() {
+ assertTrue(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ assertTrue(mWifiConnectivityHelper.isFirmwareRoamingSupported());
+ assertEquals(MAX_BSSID_BLACKLIST_SIZE, mWifiConnectivityHelper.getMaxNumBlacklistBssid());
+ assertEquals(MAX_SSID_WHITELIST_SIZE, mWifiConnectivityHelper.getMaxNumWhitelistSsid());
+ }
+
+ /**
+ * Verify that firmware roaming is set to not supported if WifiNative returned firmware roaming
+ * is supported but failed to return roaming capabilities. Firmware roaming capabilty values
+ * should be reset to INVALID_LIST_SIZE.
+ */
+ @Test
+ public void verifyFirmwareRoamingCapabilityWithFailureNativeCall() {
+ doAnswer(new AnswerWithArguments() {
+ public boolean answer(WifiNative.RoamingCapabilities roamCap) throws Exception {
+ return false;
+ }}).when(mWifiNative).getRoamingCapabilities(anyObject());
+ assertFalse(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ assertFalse(mWifiConnectivityHelper.isFirmwareRoamingSupported());
+ assertEquals(WifiConnectivityHelper.INVALID_LIST_SIZE,
+ mWifiConnectivityHelper.getMaxNumBlacklistBssid());
+ assertEquals(WifiConnectivityHelper.INVALID_LIST_SIZE,
+ mWifiConnectivityHelper.getMaxNumWhitelistSsid());
+ }
+
+ /**
+ * Verify that firmware roaming is set to not supported if WifiNative returned firmware roaming
+ * is supported but returned invalid max BSSID balcklist size. Firmware roaming capabilty values
+ * should be reset to INVALID_LIST_SIZE.
+ */
+ @Test
+ public void verifyFirmwareRoamingCapabilityWithInvalidMaxBssidBlacklistSize() {
+ doAnswer(new AnswerWithArguments() {
+ public boolean answer(WifiNative.RoamingCapabilities roamCap) throws Exception {
+ roamCap.maxBlacklistSize = -5;
+ roamCap.maxWhitelistSize = MAX_SSID_WHITELIST_SIZE;
+ return true;
+ }}).when(mWifiNative).getRoamingCapabilities(anyObject());
+ assertFalse(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ assertFalse(mWifiConnectivityHelper.isFirmwareRoamingSupported());
+ assertEquals(WifiConnectivityHelper.INVALID_LIST_SIZE,
+ mWifiConnectivityHelper.getMaxNumBlacklistBssid());
+ assertEquals(WifiConnectivityHelper.INVALID_LIST_SIZE,
+ mWifiConnectivityHelper.getMaxNumWhitelistSsid());
+ }
+
+ /**
+ * Verify that firmware roaming is set to not supported if WifiNative returned firmware roaming
+ * is supported but returned invalid max SSID whitelist size. Firmware roaming capabilty values
+ * should be reset to INVALID_LIST_SIZE.
+ */
+ @Test
+ public void verifyFirmwareRoamingCapabilityWithInvalidMaxSsidWhitelistSize() {
+ doAnswer(new AnswerWithArguments() {
+ public boolean answer(WifiNative.RoamingCapabilities roamCap) throws Exception {
+ roamCap.maxBlacklistSize = MAX_BSSID_BLACKLIST_SIZE;
+ roamCap.maxWhitelistSize = -2;
+ return true;
+ }}).when(mWifiNative).getRoamingCapabilities(anyObject());
+ assertFalse(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ assertFalse(mWifiConnectivityHelper.isFirmwareRoamingSupported());
+ assertEquals(WifiConnectivityHelper.INVALID_LIST_SIZE,
+ mWifiConnectivityHelper.getMaxNumBlacklistBssid());
+ assertEquals(WifiConnectivityHelper.INVALID_LIST_SIZE,
+ mWifiConnectivityHelper.getMaxNumWhitelistSsid());
+ }
+
+ /**
+ * Verify that correct size BSSID blacklist and SSID whitelist are accepted.
+ */
+ @Test
+ public void verifySetFirmwareRoamingConfigurationWithGoodInput() {
+ assertTrue(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ ArrayList<String> blacklist = buildBssidBlacklist(MAX_BSSID_BLACKLIST_SIZE);
+ ArrayList<String> whitelist = buildSsidWhitelist(MAX_SSID_WHITELIST_SIZE);
+ assertTrue(mWifiConnectivityHelper.setFirmwareRoamingConfiguration(blacklist, whitelist));
+
+ blacklist = buildBssidBlacklist(MAX_BSSID_BLACKLIST_SIZE - 2);
+ whitelist = buildSsidWhitelist(MAX_SSID_WHITELIST_SIZE - 3);
+ assertTrue(mWifiConnectivityHelper.setFirmwareRoamingConfiguration(blacklist, whitelist));
+ }
+
+ /**
+ * Verify that null BSSID blacklist or SSID whitelist is rejected.
+ */
+ @Test
+ public void verifySetFirmwareRoamingConfigurationWithNullInput() {
+ assertTrue(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ ArrayList<String> blacklist = buildBssidBlacklist(MAX_BSSID_BLACKLIST_SIZE);
+ ArrayList<String> whitelist = buildSsidWhitelist(MAX_SSID_WHITELIST_SIZE);
+ assertFalse(mWifiConnectivityHelper.setFirmwareRoamingConfiguration(null, whitelist));
+ assertFalse(mWifiConnectivityHelper.setFirmwareRoamingConfiguration(blacklist, null));
+ }
+
+ /**
+ * Verify that incorrect size BSSID blacklist is rejected.
+ */
+ @Test
+ public void verifySetFirmwareRoamingConfigurationWithIncorrectBlacklist() {
+ assertTrue(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ ArrayList<String> blacklist = buildBssidBlacklist(MAX_BSSID_BLACKLIST_SIZE + 1);
+ ArrayList<String> whitelist = buildSsidWhitelist(MAX_SSID_WHITELIST_SIZE);
+ assertFalse(mWifiConnectivityHelper.setFirmwareRoamingConfiguration(blacklist, whitelist));
+ }
+
+ /**
+ * Verify that incorrect size SSID whitelist is rejected.
+ */
+ @Test
+ public void verifySetFirmwareRoamingConfigurationWithIncorrectWhitelist() {
+ assertTrue(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ ArrayList<String> blacklist = buildBssidBlacklist(MAX_BSSID_BLACKLIST_SIZE);
+ ArrayList<String> whitelist = buildSsidWhitelist(MAX_SSID_WHITELIST_SIZE + 1);
+ assertFalse(mWifiConnectivityHelper.setFirmwareRoamingConfiguration(blacklist, whitelist));
+ }
+
+ /**
+ * Verify that empty BSSID blacklist and SSID whitelist are sent to WifiNative
+ * to reset the firmware roaming configuration.
+ */
+ @Test
+ public void verifySetFirmwareRoamingConfigurationWithEmptyBlacklistAndWhitelist() {
+ assertTrue(mWifiConnectivityHelper.getFirmwareRoamingInfo());
+ ArrayList<String> blacklist = buildBssidBlacklist(0);
+ ArrayList<String> whitelist = buildSsidWhitelist(0);
+ assertTrue(mWifiConnectivityHelper.setFirmwareRoamingConfiguration(blacklist, whitelist));
+ verify(mWifiNative).configureRoaming(mRoamingConfigCaptor.capture());
+ assertEquals(0, mRoamingConfigCaptor.getValue().blacklistBssids.size());
+ assertEquals(0, mRoamingConfigCaptor.getValue().whitelistSsids.size());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index 281ffa1..68c2ca3 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -22,14 +22,17 @@
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.app.test.TestAlarmManager;
import android.content.Context;
import android.content.res.Resources;
+import android.net.NetworkScoreManager;
import android.net.wifi.ScanResult;
import android.net.wifi.ScanResult.InformationElement;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
+import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiScanner.PnoScanListener;
import android.net.wifi.WifiScanner.PnoSettings;
@@ -39,47 +42,54 @@
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 android.util.LocalLog;
import com.android.internal.R;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}.
*/
@SmallTest
public class WifiConnectivityManagerTest {
-
/**
* Called before each test
*/
@Before
public void setUp() throws Exception {
- mWifiInjector = mockWifiInjector();
+ MockitoAnnotations.initMocks(this);
mResource = mockResource();
- mAlarmManager = new MockAlarmManager();
+ mAlarmManager = new TestAlarmManager();
mContext = mockContext();
+ mLocalLog = new LocalLog(512);
mWifiStateMachine = mockWifiStateMachine();
mWifiConfigManager = mockWifiConfigManager();
mWifiInfo = getWifiInfo();
mScanData = mockScanData();
mWifiScanner = mockWifiScanner();
- mWifiQNS = mockWifiQualifiedNetworkSelector();
- mWifiConnectivityManager = new WifiConnectivityManager(mContext, mWifiStateMachine,
- mWifiScanner, mWifiConfigManager, mWifiInfo, mWifiQNS, mWifiInjector,
- mLooper.getLooper(), true);
+ mWifiConnectivityHelper = mockWifiConnectivityHelper();
+ mWifiNS = mockWifiNetworkSelector();
+ mWifiConnectivityManager = createConnectivityManager();
+ verify(mWifiConfigManager).setOnSavedNetworkUpdateListener(anyObject());
mWifiConnectivityManager.setWifiEnabled(true);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
}
/**
@@ -91,33 +101,50 @@
}
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 WifiNetworkSelector mWifiNS;
private WifiStateMachine mWifiStateMachine;
private WifiScanner mWifiScanner;
+ private WifiConnectivityHelper mWifiConnectivityHelper;
private ScanData mScanData;
private WifiConfigManager mWifiConfigManager;
private WifiInfo mWifiInfo;
- private Clock mClock = mock(Clock.class);
- private WifiLastResortWatchdog mWifiLastResortWatchdog;
- private WifiMetrics mWifiMetrics;
- private WifiInjector mWifiInjector;
+ private LocalLog mLocalLog;
+ @Mock private FrameworkFacade mFrameworkFacade;
+ @Mock private NetworkScoreManager mNetworkScoreManager;
+ @Mock private Clock mClock;
+ @Mock private WifiLastResortWatchdog mWifiLastResortWatchdog;
+ @Mock private WifiMetrics mWifiMetrics;
+ @Mock private WifiNetworkScoreCache mScoreCache;
+ @Captor ArgumentCaptor<ScanResult> mCandidateScanResultCaptor;
+ @Captor ArgumentCaptor<ArrayList<String>> mBssidBlacklistCaptor;
+ @Captor ArgumentCaptor<ArrayList<String>> mSsidWhitelistCaptor;
+ private MockResources mResources;
private static final int CANDIDATE_NETWORK_ID = 0;
private static final String CANDIDATE_SSID = "\"AnSsid\"";
private static final String CANDIDATE_BSSID = "6c:f3:7f:ae:8c:f3";
- private static final String TAG = "WifiConnectivityManager Unit Test";
+ private static final String INVALID_SCAN_RESULT_BSSID = "6c:f3:7f:ae:8c:f4";
private static final long CURRENT_SYSTEM_TIME_MS = 1000;
+ private static final int MAX_BSSID_BLACKLIST_SIZE = 16;
+
Resources mockResource() {
Resources resource = mock(Resources.class);
when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80);
when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24);
-
+ when(resource.getBoolean(
+ R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true);
+ when(resource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz))
+ .thenReturn(-60);
+ when(resource.getInteger(
+ R.integer.config_wifi_framework_current_network_boost)).thenReturn(16);
return resource;
}
@@ -151,17 +178,17 @@
// do a synchronous answer for the ScanListener callbacks
doAnswer(new AnswerWithArguments() {
- public void answer(ScanSettings settings, ScanListener listener,
- WorkSource workSource) throws Exception {
- listener.onResults(scanDatas);
- }}).when(scanner).startBackgroundScan(anyObject(), anyObject(), anyObject());
+ public void answer(ScanSettings settings, ScanListener listener,
+ WorkSource workSource) throws Exception {
+ listener.onResults(scanDatas);
+ }}).when(scanner).startBackgroundScan(anyObject(), anyObject(), anyObject());
doAnswer(new AnswerWithArguments() {
- public void answer(ScanSettings settings, ScanListener listener,
- WorkSource workSource) throws Exception {
- listener.onResults(scanDatas);
- allSingleScanListenerCaptor.getValue().onResults(scanDatas);
- }}).when(scanner).startScan(anyObject(), anyObject(), anyObject());
+ public void answer(ScanSettings settings, ScanListener listener,
+ WorkSource workSource) throws Exception {
+ listener.onResults(scanDatas);
+ allSingleScanListenerCaptor.getValue().onResults(scanDatas);
+ }}).when(scanner).startScan(anyObject(), anyObject(), anyObject());
// This unfortunately needs to be a somewhat valid scan result, otherwise
// |ScanDetailUtil.toScanDetail| raises exceptions.
@@ -173,7 +200,7 @@
scanResults[0].informationElements[0] = new InformationElement();
scanResults[0].informationElements[0].id = InformationElement.EID_SSID;
scanResults[0].informationElements[0].bytes =
- CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8);
+ CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8);
doAnswer(new AnswerWithArguments() {
public void answer(ScanSettings settings, PnoSettings pnoSettings,
@@ -190,10 +217,18 @@
return scanner;
}
+ WifiConnectivityHelper mockWifiConnectivityHelper() {
+ WifiConnectivityHelper connectivityHelper = mock(WifiConnectivityHelper.class);
+
+ when(connectivityHelper.isFirmwareRoamingSupported()).thenReturn(false);
+ when(connectivityHelper.getMaxNumBlacklistBssid()).thenReturn(MAX_BSSID_BLACKLIST_SIZE);
+
+ return connectivityHelper;
+ }
+
WifiStateMachine mockWifiStateMachine() {
WifiStateMachine stateMachine = mock(WifiStateMachine.class);
- when(stateMachine.getFrequencyBand()).thenReturn(1);
when(stateMachine.isLinkDebouncing()).thenReturn(false);
when(stateMachine.isConnected()).thenReturn(false);
when(stateMachine.isDisconnected()).thenReturn(true);
@@ -202,20 +237,20 @@
return stateMachine;
}
- WifiQualifiedNetworkSelector mockWifiQualifiedNetworkSelector() {
- WifiQualifiedNetworkSelector qns = mock(WifiQualifiedNetworkSelector.class);
+ WifiNetworkSelector mockWifiNetworkSelector() {
+ WifiNetworkSelector ns = mock(WifiNetworkSelector.class);
WifiConfiguration candidate = generateWifiConfig(
0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
- candidate.BSSID = CANDIDATE_BSSID;
+ candidate.BSSID = WifiStateMachine.SUPPLICANT_BSSID_ANY;
ScanResult candidateScanResult = new ScanResult();
candidateScanResult.SSID = CANDIDATE_SSID;
candidateScanResult.BSSID = CANDIDATE_BSSID;
candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
- when(qns.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
- anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(candidate);
- return qns;
+ when(ns.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
+ anyBoolean(), anyBoolean())).thenReturn(candidate);
+ return ns;
}
WifiInfo getWifiInfo() {
@@ -231,38 +266,30 @@
WifiConfigManager mockWifiConfigManager() {
WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class);
- when(wifiConfigManager.getWifiConfiguration(anyInt())).thenReturn(null);
- when(wifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- wifiConfigManager.mThresholdSaturatedRssi24 = new AtomicInteger(
- WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND);
- wifiConfigManager.mCurrentNetworkBoost = new AtomicInteger(
- WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD);
+ when(wifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(null);
// Pass dummy pno network list, otherwise Pno scan requests will not be triggered.
PnoSettings.PnoNetwork pnoNetwork = new PnoSettings.PnoNetwork(CANDIDATE_SSID);
ArrayList<PnoSettings.PnoNetwork> pnoNetworkList = new ArrayList<>();
pnoNetworkList.add(pnoNetwork);
- when(wifiConfigManager.retrieveDisconnectedPnoNetworkList()).thenReturn(pnoNetworkList);
- when(wifiConfigManager.retrieveConnectedPnoNetworkList()).thenReturn(pnoNetworkList);
+ when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList);
+ when(wifiConfigManager.retrievePnoNetworkList()).thenReturn(pnoNetworkList);
return wifiConfigManager;
}
- WifiInjector mockWifiInjector() {
- WifiInjector wifiInjector = mock(WifiInjector.class);
- mWifiLastResortWatchdog = mock(WifiLastResortWatchdog.class);
- mWifiMetrics = mock(WifiMetrics.class);
- when(wifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
- when(wifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
- when(wifiInjector.getClock()).thenReturn(mClock);
- return wifiInjector;
+ WifiConnectivityManager createConnectivityManager() {
+ return new WifiConnectivityManager(mContext, mWifiStateMachine, mWifiScanner,
+ mWifiConfigManager, mWifiInfo, mWifiNS, mWifiConnectivityHelper,
+ mWifiLastResortWatchdog, mWifiMetrics, mLooper.getLooper(), mClock,
+ mLocalLog, true, mFrameworkFacade, null, null, null);
}
/**
* Wifi enters disconnected state while screen is on.
*
* Expected behavior: WifiConnectivityManager calls
- * WifiStateMachine.autoConnectToNetwork() with the
+ * WifiStateMachine.startConnectToNetwork() with the
* expected candidate network ID and BSSID.
*/
@Test
@@ -274,15 +301,14 @@
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
- verify(mWifiStateMachine).autoConnectToNetwork(
- CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+ verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
/**
* Wifi enters connected state while screen is on.
*
* Expected behavior: WifiConnectivityManager calls
- * WifiStateMachine.autoConnectToNetwork() with the
+ * WifiStateMachine.startConnectToNetwork() with the
* expected candidate network ID and BSSID.
*/
@Test
@@ -294,15 +320,14 @@
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_CONNECTED);
- verify(mWifiStateMachine).autoConnectToNetwork(
- CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+ verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
/**
* Screen turned on while WiFi in disconnected state.
*
* Expected behavior: WifiConnectivityManager calls
- * WifiStateMachine.autoConnectToNetwork() with the
+ * WifiStateMachine.startConnectToNetwork() with the
* expected candidate network ID and BSSID.
*/
@Test
@@ -314,7 +339,7 @@
// Set screen to on
mWifiConnectivityManager.handleScreenStateChanged(true);
- verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork(
+ verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork(
CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
@@ -322,7 +347,7 @@
* Screen turned on while WiFi in connected state.
*
* Expected behavior: WifiConnectivityManager calls
- * WifiStateMachine.autoConnectToNetwork() with the
+ * WifiStateMachine.startConnectToNetwork() with the
* expected candidate network ID and BSSID.
*/
@Test
@@ -334,7 +359,7 @@
// Set screen to on
mWifiConnectivityManager.handleScreenStateChanged(true);
- verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork(
+ verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork(
CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
@@ -343,29 +368,32 @@
* auto roaming is disabled.
*
* Expected behavior: WifiConnectivityManager doesn't invoke
- * WifiStateMachine.autoConnectToNetwork() because roaming
+ * WifiStateMachine.startConnectToNetwork() because roaming
* is turned off.
*/
@Test
public void turnScreenOnWhenWifiInConnectedStateRoamingDisabled() {
+ // Turn off auto roaming
+ when(mResource.getBoolean(
+ R.bool.config_wifi_framework_enable_associated_network_selection))
+ .thenReturn(false);
+ mWifiConnectivityManager = createConnectivityManager();
+
// Set WiFi to connected state
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_CONNECTED);
- // Turn off auto roaming
- when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(false);
-
// Set screen to on
mWifiConnectivityManager.handleScreenStateChanged(true);
- verify(mWifiStateMachine, times(0)).autoConnectToNetwork(
+ verify(mWifiStateMachine, times(0)).startConnectToNetwork(
CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
/**
* Multiple back to back connection attempts within the rate interval should be rate limited.
*
- * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork()
+ * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork()
* with the expected candidate network ID and BSSID for only the expected number of times within
* the given interval.
*/
@@ -382,7 +410,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);
@@ -390,13 +418,13 @@
}
// 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);
// Verify that we attempt to connect upto the rate.
- verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork(
+ verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
@@ -404,7 +432,7 @@
* Multiple back to back connection attempts outside the rate interval should not be rate
* limited.
*
- * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork()
+ * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork()
* with the expected candidate network ID and BSSID for only the expected number of times within
* the given interval.
*/
@@ -421,7 +449,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);
@@ -429,7 +457,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(
@@ -437,14 +465,14 @@
numAttempts++;
// Verify that all the connection attempts went through
- verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork(
+ verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
/**
* Multiple back to back connection attempts after a user selection should not be rate limited.
*
- * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork()
+ * Expected behavior: WifiConnectivityManager calls WifiStateMachine.startConnectToNetwork()
* with the expected candidate network ID and BSSID for only the expected number of times within
* the given interval.
*/
@@ -461,18 +489,19 @@
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);
numAttempts++;
}
- mWifiConnectivityManager.connectToUserSelectNetwork(CANDIDATE_NETWORK_ID, false);
+ mWifiConnectivityManager.setUserConnectChoice(CANDIDATE_NETWORK_ID);
+ mWifiConnectivityManager.prepareForForcedConnection(CANDIDATE_NETWORK_ID);
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);
@@ -480,7 +509,7 @@
}
// Verify that all the connection attempts went through
- verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork(
+ verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
@@ -492,9 +521,10 @@
* because of their low RSSI values.
*/
@Test
- public void PnoRetryForLowRssiNetwork() {
- when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
- anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null);
+ @Ignore("b/32977707")
+ public void pnoRetryForLowRssiNetwork() {
+ when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
+ anyBoolean(), anyBoolean())).thenReturn(null);
// Set screen to off
mWifiConnectivityManager.handleScreenStateChanged(false);
@@ -513,7 +543,7 @@
.getLowRssiNetworkRetryDelay();
assertEquals(lowRssiNetworkRetryDelayStartValue * 2,
- lowRssiNetworkRetryDelayAfterPnoValue);
+ lowRssiNetworkRetryDelayAfterPnoValue);
}
/**
@@ -523,6 +553,7 @@
* a candidate while watchdog single scan did.
*/
@Test
+ @Ignore("b/32977707")
public void watchdogBitePnoBadIncrementsMetrics() {
// Set screen to off
mWifiConnectivityManager.handleScreenStateChanged(false);
@@ -546,10 +577,11 @@
* a candidate which was the same with watchdog single scan.
*/
@Test
+ @Ignore("b/32977707")
public void watchdogBitePnoGoodIncrementsMetrics() {
// Qns returns no candidate after watchdog single scan.
- when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
- anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null);
+ when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
+ anyBoolean(), anyBoolean())).thenReturn(null);
// Set screen to off
mWifiConnectivityManager.handleScreenStateChanged(false);
@@ -576,7 +608,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);
@@ -584,7 +616,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(
@@ -592,12 +624,12 @@
// Get the first periodic scan interval
long firstIntervalMs = mAlarmManager
- .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
- - currentTimeStamp;
+ .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
+ - currentTimeStamp;
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);
@@ -605,14 +637,14 @@
// Get the second periodic scan interval
long secondIntervalMs = mAlarmManager
- .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
- - currentTimeStamp;
+ .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
+ - currentTimeStamp;
// Verify the intervals are exponential back off
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;
@@ -623,7 +655,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);
@@ -639,7 +671,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);
@@ -647,7 +679,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(
@@ -655,12 +687,12 @@
// Get the first periodic scan interval
long firstIntervalMs = mAlarmManager
- .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
- - currentTimeStamp;
+ .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
+ - currentTimeStamp;
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);
@@ -668,14 +700,14 @@
// Get the second periodic scan interval
long secondIntervalMs = mAlarmManager
- .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
- - currentTimeStamp;
+ .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
+ - currentTimeStamp;
// Verify the intervals are exponential back off
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;
@@ -686,23 +718,23 @@
.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);
}
/**
- * When screen on trigger two connection state change events back to back to
- * verify that the minium scan interval is enforced.
+ * When screen on trigger a disconnected state change event then a connected state
+ * change event back to back to verify that the minium scan interval is enforced.
*
* Expected behavior: WifiConnectivityManager start the second periodic single
* scan PERIODIC_SCAN_INTERVAL_MS after the first one.
*/
@Test
- public void checkMinimumPeriodicScanIntervalWhenScreenOn() {
+ public void checkMinimumPeriodicScanIntervalWhenScreenOnAndConnected() {
long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set screen to ON
mWifiConnectivityManager.handleScreenStateChanged(true);
@@ -710,30 +742,79 @@
// 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;
- long firstScanTimeStamp = currentTimeStamp;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ long scanForDisconnectedTimeStamp = currentTimeStamp;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
+
+ // Set WiFi to disconnected state which triggers a scan immediately
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+ verify(mWifiScanner, times(1)).startScan(anyObject(), anyObject(), anyObject());
+
+ // Set up time stamp for when entering CONNECTED state
+ currentTimeStamp += 2000;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
+
+ // Set WiFi to connected state to trigger its periodic scan
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_CONNECTED);
+
+ // The very first scan triggered for connected state is actually via the alarm timer
+ // and it obeys the minimum scan interval
+ long firstScanForConnectedTimeStamp = mAlarmManager
+ .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
+
+ // Verify that the first scan for connected state is scheduled PERIODIC_SCAN_INTERVAL_MS
+ // after the scan for disconnected state
+ assertEquals(firstScanForConnectedTimeStamp, scanForDisconnectedTimeStamp
+ + WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
+ }
+
+ /**
+ * When screen on trigger a connected state change event then a disconnected state
+ * change event back to back to verify that a scan is fired immediately for the
+ * disconnected state change event.
+ *
+ * Expected behavior: WifiConnectivityManager directly starts the periodic immediately
+ * for the disconnected state change event. The second scan for disconnected state is
+ * via alarm timer.
+ */
+ @Test
+ public void scanImmediatelyWhenScreenOnAndDisconnected() {
+ long currentTimeStamp = CURRENT_SYSTEM_TIME_MS;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
+
+ // Set screen to ON
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ // 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;
+ long scanForConnectedTimeStamp = currentTimeStamp;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Set WiFi to connected state to trigger the periodic scan
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_CONNECTED);
+ verify(mWifiScanner, times(1)).startScan(anyObject(), anyObject(), anyObject());
- // Set the second scan attempt time stamp.
+ // Set up the time stamp for when entering DISCONNECTED state
currentTimeStamp += 2000;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ long enteringDisconnectedStateTimeStamp = currentTimeStamp;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
- // Set WiFi to disconnected state to trigger another periodic scan
+ // Set WiFi to disconnected state to trigger its periodic scan
mWifiConnectivityManager.handleConnectionStateChanged(
WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
- // Get the second periodic scan actual time stamp
- long secondScanTimeStamp = mAlarmManager
- .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
+ // Verify the very first scan for DISCONNECTED state is fired immediately
+ verify(mWifiScanner, times(2)).startScan(anyObject(), anyObject(), anyObject());
+ long secondScanForDisconnectedTimeStamp = mAlarmManager
+ .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
- // Verify that the second scan is scheduled PERIODIC_SCAN_INTERVAL_MS after the
- // very first scan.
- assertEquals(secondScanTimeStamp, firstScanTimeStamp
- + WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
-
+ // Verify that the second scan is scheduled PERIODIC_SCAN_INTERVAL_MS after
+ // entering DISCONNECTED state.
+ assertEquals(secondScanForDisconnectedTimeStamp, enteringDisconnectedStateTimeStamp
+ + WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
}
/**
@@ -747,7 +828,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);
@@ -756,7 +837,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(
@@ -764,7 +845,7 @@
// Set the second scan attempt time stamp
currentTimeStamp += 2000;
- when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeStamp);
// Allow untrusted networks so WifiConnectivityManager starts a periodic scan
// immediately.
@@ -796,15 +877,13 @@
when(mWifiStateMachine.getCurrentWifiConfiguration())
.thenReturn(new WifiConfiguration());
- when(mWifiStateMachine.getFrequencyBand())
- .thenReturn(WifiManager.WIFI_FREQUENCY_BAND_5GHZ);
- when(mWifiConfigManager.makeChannelList(any(WifiConfiguration.class), anyInt()))
- .thenReturn(channelList);
+ when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
+ anyInt())).thenReturn(channelList);
doAnswer(new AnswerWithArguments() {
public void answer(ScanSettings settings, ScanListener listener,
WorkSource workSource) throws Exception {
- assertEquals(settings.band, WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS);
+ assertEquals(settings.band, WifiScanner.WIFI_BAND_BOTH_WITH_DFS);
assertNull(settings.channels);
}}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
@@ -827,8 +906,8 @@
*/
@Test
public void checkSingleScanSettingsWhenConnectedWithHighDataRate() {
- mWifiInfo.txSuccessRate = WifiConfigManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
- mWifiInfo.rxSuccessRate = WifiConfigManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
+ mWifiInfo.txSuccessRate = WifiConnectivityManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
+ mWifiInfo.rxSuccessRate = WifiConnectivityManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
final HashSet<Integer> channelList = new HashSet<>();
channelList.add(1);
@@ -837,8 +916,8 @@
when(mWifiStateMachine.getCurrentWifiConfiguration())
.thenReturn(new WifiConfiguration());
- when(mWifiConfigManager.makeChannelList(any(WifiConfiguration.class), anyInt()))
- .thenReturn(channelList);
+ when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
+ anyInt())).thenReturn(channelList);
doAnswer(new AnswerWithArguments() {
public void answer(ScanSettings settings, ScanListener listener,
@@ -869,22 +948,20 @@
*/
@Test
public void checkSingleScanSettingsWhenConnectedWithHighDataRateNotInCache() {
- mWifiInfo.txSuccessRate = WifiConfigManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
- mWifiInfo.rxSuccessRate = WifiConfigManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
+ mWifiInfo.txSuccessRate = WifiConnectivityManager.MAX_TX_PACKET_FOR_FULL_SCANS * 2;
+ mWifiInfo.rxSuccessRate = WifiConnectivityManager.MAX_RX_PACKET_FOR_FULL_SCANS * 2;
final HashSet<Integer> channelList = new HashSet<>();
when(mWifiStateMachine.getCurrentWifiConfiguration())
.thenReturn(new WifiConfiguration());
- when(mWifiStateMachine.getFrequencyBand())
- .thenReturn(WifiManager.WIFI_FREQUENCY_BAND_5GHZ);
- when(mWifiConfigManager.makeChannelList(any(WifiConfiguration.class), anyInt()))
- .thenReturn(channelList);
+ when(mWifiConfigManager.fetchChannelSetForNetworkForPartialScan(anyInt(), anyLong(),
+ anyInt())).thenReturn(channelList);
doAnswer(new AnswerWithArguments() {
public void answer(ScanSettings settings, ScanListener listener,
WorkSource workSource) throws Exception {
- assertEquals(settings.band, WifiScanner.WIFI_BAND_5_GHZ_WITH_DFS);
+ assertEquals(settings.band, WifiScanner.WIFI_BAND_BOTH_WITH_DFS);
assertNull(settings.channels);
}}).when(mWifiScanner).startScan(anyObject(), anyObject(), anyObject());
@@ -939,7 +1016,7 @@
* act on them.
*
* Expected behavior: WifiConnectivityManager calls
- * WifiStateMachine.autoConnectToNetwork() with the
+ * WifiStateMachine.startConnectToNetwork() with the
* expected candidate network ID and BSSID.
*/
@Test
@@ -952,8 +1029,7 @@
// Verify that WCM receives the scan results and initiates a connection
// to the network.
- verify(mWifiStateMachine).autoConnectToNetwork(
- CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+ verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
/**
@@ -961,7 +1037,7 @@
* results.
*
* Expected behavior: WifiConnectivityManager doesn't invoke
- * WifiStateMachine.autoConnectToNetwork() when full band scan
+ * WifiStateMachine.startConnectToNetwork() when full band scan
* results are not available.
*/
@Test
@@ -978,7 +1054,7 @@
mWifiConnectivityManager.forceConnectivityScan();
// No roaming because no full band scan results.
- verify(mWifiStateMachine, times(0)).autoConnectToNetwork(
+ verify(mWifiStateMachine, times(0)).startConnectToNetwork(
CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
// Set up as full band scan results.
@@ -989,7 +1065,493 @@
mWifiConnectivityManager.forceConnectivityScan();
// Roaming attempt because full band scan results are available.
- verify(mWifiStateMachine).autoConnectToNetwork(
+ verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+ }
+
+ /**
+ * Verify the BSSID blacklist implementation.
+ *
+ * Expected behavior: A BSSID gets blacklisted after being disabled
+ * for 3 times, and becomes available after being re-enabled. Firmware
+ * controlled roaming is supported, its roaming configuration needs to be
+ * updated as well.
+ */
+ @Test
+ public void blacklistAndReenableBssid() {
+ String bssid = "6c:f3:7f:ae:8c:f3";
+
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+ // Verify that a BSSID gets blacklisted only after being disabled
+ // for BSSID_BLACKLIST_THRESHOLD times for reasons other than
+ // REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA.
+ for (int i = 0; i < WifiConnectivityManager.BSSID_BLACKLIST_THRESHOLD; i++) {
+ assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
+ mWifiConnectivityManager.trackBssid(bssid, false, 1);
+ }
+
+ // Verify the BSSID is now blacklisted.
+ assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
+ // Verify the BSSID gets sent to firmware.
+ verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+
+ // Re-enable the bssid.
+ mWifiConnectivityManager.trackBssid(bssid, true, 1);
+
+ // Verify the bssid is no longer blacklisted.
+ assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
+ // Verify the BSSID gets cleared from firmware.
+ verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertFalse(mBssidBlacklistCaptor.getValue().contains(bssid));
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+ }
+
+ /**
+ * Verify that a network gets blacklisted immediately if it is unable
+ * to handle new stations.
+ */
+ @Test
+ public void blacklistNetworkImmediatelyIfApHasNoCapacityForNewStation() {
+ String bssid = "6c:f3:7f:ae:8c:f3";
+
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+ // Blacklist the BSSID
+ mWifiConnectivityManager.trackBssid(bssid, false,
+ WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
+
+ // Verify the BSSID is now blacklisted.
+ assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
+ // Verify the BSSID gets sent to firmware.
+ verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+ }
+
+ /**
+ * Verify that a blacklisted BSSID becomes available only after
+ * BSSID_BLACKLIST_EXPIRE_TIME_MS.
+ */
+ @Test
+ public void verifyBlacklistRefreshedAfterScanResults() {
+ String bssid = "6c:f3:7f:ae:8c:f3";
+
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+ // Blacklist the BSSID.
+ mWifiConnectivityManager.trackBssid(bssid, false,
+ WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
+
+ // Verify the BSSID is now blacklisted.
+ assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
+ // Verify the BSSID gets sent to firmware.
+ verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+
+ // Force a connectivity scan in less than BSSID_BLACKLIST_EXPIRE_TIME_MS.
+ // Arrival of scan results will trigger WifiConnectivityManager to refresh its
+ // BSSID blacklist. Verify that the blacklisted BSSId is not freed because
+ // its blacklist expiration time hasn't reached yet.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiConnectivityManager.BSSID_BLACKLIST_EXPIRE_TIME_MS / 2);
+ mWifiConnectivityManager.forceConnectivityScan();
+ assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
+
+ // Force another connectivity scan at BSSID_BLACKLIST_EXPIRE_TIME_MS from when the
+ // BSSID was blacklisted. Verify that the blacklisted BSSId is freed.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiConnectivityManager.BSSID_BLACKLIST_EXPIRE_TIME_MS);
+ mWifiConnectivityManager.forceConnectivityScan();
+
+ // Verify the BSSID is no longer blacklisted.
+ assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
+ // Verify the BSSID gets cleared from firmware.
+ verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertFalse(mBssidBlacklistCaptor.getValue().contains(bssid));
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+ }
+
+ /**
+ * Verify that BSSID blacklist gets cleared when exiting Wifi client mode.
+ */
+ @Test
+ public void clearBssidBlacklistWhenExitingWifiClientMode() {
+ String bssid = "6c:f3:7f:ae:8c:f3";
+
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+
+ // Blacklist the BSSID.
+ mWifiConnectivityManager.trackBssid(bssid, false,
+ WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
+
+ // Verify the BSSID is now blacklisted.
+ assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
+ // Verify the BSSID gets sent to firmware.
+ verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+
+ // Exit Wifi client mode.
+ mWifiConnectivityManager.setWifiEnabled(false);
+
+ // Verify the BSSID blacklist is empty.
+ assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
+ verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertTrue(mBssidBlacklistCaptor.getValue().isEmpty());
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+ }
+
+ /**
+ * Verify that BSSID blacklist gets cleared when preparing for a forced connection
+ * initiated by user/app.
+ */
+ @Test
+ public void clearBssidBlacklistWhenPreparingForForcedConnection() {
+ String bssid = "6c:f3:7f:ae:8c:f3";
+
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+
+ // Blacklist the BSSID.
+ mWifiConnectivityManager.trackBssid(bssid, false,
+ WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
+
+ // Verify the BSSID is now blacklisted.
+ assertTrue(mWifiConnectivityManager.isBssidDisabled(bssid));
+ // Verify the BSSID gets sent to firmware.
+ verify(mWifiConnectivityHelper).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertTrue(mBssidBlacklistCaptor.getValue().contains(bssid));
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+
+ // Prepare for a forced connection attempt.
+ mWifiConnectivityManager.prepareForForcedConnection(1);
+
+ // Verify the BSSID blacklist is empty.
+ assertFalse(mWifiConnectivityManager.isBssidDisabled(bssid));
+ verify(mWifiConnectivityHelper, times(2)).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertTrue(mBssidBlacklistCaptor.getValue().isEmpty());
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+ }
+
+ /**
+ /**
+ * Verify that BSSID blacklist gets trimmed down to fit firmware capability.
+ */
+ @Test
+ public void trimDownBssidBlacklistForFirmware() {
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+
+ // Blacklist more than MAX_BSSID_BLACKLIST_SIZE BSSIDs.
+ for (int i = 0; i < MAX_BSSID_BLACKLIST_SIZE + 6; i++) {
+ StringBuilder bssid = new StringBuilder("55:44:33:22:11:00");
+ bssid.setCharAt(16, (char) ('0' + i));
+ mWifiConnectivityManager.trackBssid(bssid.toString(), false,
+ WifiConnectivityManager.REASON_CODE_AP_UNABLE_TO_HANDLE_NEW_STA);
+ // Verify that up to MAX_BSSID_BLACKLIST_SIZE BSSIDs gets sent to firmware.
+ verify(mWifiConnectivityHelper, times(i + 1)).setFirmwareRoamingConfiguration(
+ mBssidBlacklistCaptor.capture(), mSsidWhitelistCaptor.capture());
+ assertEquals((i + 1) < MAX_BSSID_BLACKLIST_SIZE ? (i + 1) : MAX_BSSID_BLACKLIST_SIZE,
+ mBssidBlacklistCaptor.getValue().size());
+ assertTrue(mSsidWhitelistCaptor.getValue().isEmpty());
+ }
+ }
+
+ /**
+ * When WifiConnectivityManager is on and Wifi client mode is enabled, framework
+ * queries firmware via WifiConnectivityHelper to check if firmware roaming is
+ * supported and its capability.
+ *
+ * Expected behavior: WifiConnectivityManager#setWifiEnabled calls into
+ * WifiConnectivityHelper#getFirmwareRoamingInfo
+ */
+ @Test
+ public void verifyGetFirmwareRoamingInfoIsCalledWhenEnableWiFiAndWcmOn() {
+ reset(mWifiConnectivityHelper);
+ // WifiConnectivityManager is on by default
+ mWifiConnectivityManager.setWifiEnabled(true);
+ verify(mWifiConnectivityHelper).getFirmwareRoamingInfo();
+ }
+
+ /**
+ * When WifiConnectivityManager is off, verify that framework does not
+ * query firmware via WifiConnectivityHelper to check if firmware roaming is
+ * supported and its capability when enabling Wifi client mode.
+ *
+ * Expected behavior: WifiConnectivityManager#setWifiEnabled does not call into
+ * WifiConnectivityHelper#getFirmwareRoamingInfo
+ */
+ @Test
+ public void verifyGetFirmwareRoamingInfoIsNotCalledWhenEnableWiFiAndWcmOff() {
+ reset(mWifiConnectivityHelper);
+ mWifiConnectivityManager.enable(false);
+ mWifiConnectivityManager.setWifiEnabled(true);
+ verify(mWifiConnectivityHelper, times(0)).getFirmwareRoamingInfo();
+ }
+
+ /*
+ * Firmware supports controlled roaming.
+ * Connect to a network which doesn't have a config specified BSSID.
+ *
+ * Expected behavior: WifiConnectivityManager calls
+ * WifiStateMachine.startConnectToNetwork() with the
+ * expected candidate network ID, and the BSSID value should be
+ * 'any' since firmware controls the roaming.
+ */
+ @Test
+ public void useAnyBssidToConnectWhenFirmwareRoamingOnAndConfigHasNoBssidSpecified() {
+ // Firmware controls roaming
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+
+ // Set screen to on
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ // Set WiFi to disconnected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+ verify(mWifiStateMachine).startConnectToNetwork(
+ CANDIDATE_NETWORK_ID, WifiStateMachine.SUPPLICANT_BSSID_ANY);
+ }
+
+ /*
+ * Firmware supports controlled roaming.
+ * Connect to a network which has a config specified BSSID.
+ *
+ * Expected behavior: WifiConnectivityManager calls
+ * WifiStateMachine.startConnectToNetwork() with the
+ * expected candidate network ID, and the BSSID value should be
+ * the config specified one.
+ */
+ @Test
+ public void useConfigSpecifiedBssidToConnectWhenFirmwareRoamingOn() {
+ // Firmware controls roaming
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+
+ // Set up the candidate configuration such that it has a BSSID specified.
+ WifiConfiguration candidate = generateWifiConfig(
+ 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
+ candidate.BSSID = CANDIDATE_BSSID; // config specified
+ ScanResult candidateScanResult = new ScanResult();
+ candidateScanResult.SSID = CANDIDATE_SSID;
+ candidateScanResult.BSSID = CANDIDATE_BSSID;
+ candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
+
+ when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
+ anyBoolean(), anyBoolean())).thenReturn(candidate);
+
+ // Set screen to on
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ // Set WiFi to disconnected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+ verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+ }
+
+ /*
+ * Firmware does not support controlled roaming.
+ * Connect to a network which doesn't have a config specified BSSID.
+ *
+ * Expected behavior: WifiConnectivityManager calls
+ * WifiStateMachine.startConnectToNetwork() with the expected candidate network ID,
+ * and the BSSID value should be the candidate scan result specified.
+ */
+ @Test
+ public void useScanResultBssidToConnectWhenFirmwareRoamingOffAndConfigHasNoBssidSpecified() {
+ // Set screen to on
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ // Set WiFi to disconnected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+ verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+ }
+
+ /*
+ * Firmware does not support controlled roaming.
+ * Connect to a network which has a config specified BSSID.
+ *
+ * Expected behavior: WifiConnectivityManager calls
+ * WifiStateMachine.startConnectToNetwork() with the expected candidate network ID,
+ * and the BSSID value should be the config specified one.
+ */
+ @Test
+ public void useConfigSpecifiedBssidToConnectionWhenFirmwareRoamingOff() {
+ // Set up the candidate configuration such that it has a BSSID specified.
+ WifiConfiguration candidate = generateWifiConfig(
+ 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
+ candidate.BSSID = CANDIDATE_BSSID; // config specified
+ ScanResult candidateScanResult = new ScanResult();
+ candidateScanResult.SSID = CANDIDATE_SSID;
+ candidateScanResult.BSSID = CANDIDATE_BSSID;
+ candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
+
+ when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
+ anyBoolean(), anyBoolean())).thenReturn(candidate);
+
+ // Set screen to on
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ // Set WiFi to disconnected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+ verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+ }
+
+ /**
+ * Firmware does not support controlled roaming.
+ * WiFi in connected state, framework triggers roaming.
+ *
+ * Expected behavior: WifiConnectivityManager invokes
+ * WifiStateMachine.startRoamToNetwork().
+ */
+ @Test
+ public void frameworkInitiatedRoaming() {
+ // Mock the currently connected network which has the same networkID and
+ // SSID as the one to be selected.
+ WifiConfiguration currentNetwork = generateWifiConfig(
+ 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
+ when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork);
+
+ // Set WiFi to connected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_CONNECTED);
+
+ // Set screen to on
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ verify(mWifiStateMachine).startRoamToNetwork(eq(CANDIDATE_NETWORK_ID),
+ mCandidateScanResultCaptor.capture());
+ assertEquals(mCandidateScanResultCaptor.getValue().BSSID, CANDIDATE_BSSID);
+ }
+
+ /**
+ * Firmware supports controlled roaming.
+ * WiFi in connected state, framework does not trigger roaming
+ * as it's handed off to the firmware.
+ *
+ * Expected behavior: WifiConnectivityManager doesn't invoke
+ * WifiStateMachine.startRoamToNetwork().
+ */
+ @Test
+ public void noFrameworkRoamingIfConnectedAndFirmwareRoamingSupported() {
+ // Mock the currently connected network which has the same networkID and
+ // SSID as the one to be selected.
+ WifiConfiguration currentNetwork = generateWifiConfig(
+ 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
+ when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork);
+
+ // Firmware controls roaming
+ when(mWifiConnectivityHelper.isFirmwareRoamingSupported()).thenReturn(true);
+
+ // Set WiFi to connected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_CONNECTED);
+
+ // Set screen to on
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ verify(mWifiStateMachine, times(0)).startRoamToNetwork(anyInt(), anyObject());
+ }
+
+ /*
+ * Wifi in disconnected state. Drop the connection attempt if the recommended
+ * network configuration has a BSSID specified but the scan result BSSID doesn't
+ * match it.
+ *
+ * Expected behavior: WifiConnectivityManager doesn't invoke
+ * WifiStateMachine.startConnectToNetwork().
+ */
+ @Test
+ public void dropConnectAttemptIfConfigSpecifiedBssidDifferentFromScanResultBssid() {
+ // Set up the candidate configuration such that it has a BSSID specified.
+ WifiConfiguration candidate = generateWifiConfig(
+ 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
+ candidate.BSSID = CANDIDATE_BSSID; // config specified
+ ScanResult candidateScanResult = new ScanResult();
+ candidateScanResult.SSID = CANDIDATE_SSID;
+ // Set up the scan result BSSID to be different from the config specified one.
+ candidateScanResult.BSSID = INVALID_SCAN_RESULT_BSSID;
+ candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
+
+ when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
+ anyBoolean(), anyBoolean())).thenReturn(candidate);
+
+ // Set screen to on
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ // Set WiFi to disconnected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
+
+ verify(mWifiStateMachine, times(0)).startConnectToNetwork(
CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
}
+
+ /*
+ * Wifi in connected state. Drop the roaming attempt if the recommended
+ * network configuration has a BSSID specified but the scan result BSSID doesn't
+ * match it.
+ *
+ * Expected behavior: WifiConnectivityManager doesn't invoke
+ * WifiStateMachine.startRoamToNetwork().
+ */
+ @Test
+ public void dropRoamingAttemptIfConfigSpecifiedBssidDifferentFromScanResultBssid() {
+ // Mock the currently connected network which has the same networkID and
+ // SSID as the one to be selected.
+ WifiConfiguration currentNetwork = generateWifiConfig(
+ 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
+ when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(currentNetwork);
+
+ // Set up the candidate configuration such that it has a BSSID specified.
+ WifiConfiguration candidate = generateWifiConfig(
+ 0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
+ candidate.BSSID = CANDIDATE_BSSID; // config specified
+ ScanResult candidateScanResult = new ScanResult();
+ candidateScanResult.SSID = CANDIDATE_SSID;
+ // Set up the scan result BSSID to be different from the config specified one.
+ candidateScanResult.BSSID = INVALID_SCAN_RESULT_BSSID;
+ candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
+
+ when(mWifiNS.selectNetwork(anyObject(), anyObject(), anyObject(), anyBoolean(),
+ anyBoolean(), anyBoolean())).thenReturn(candidate);
+
+ // Set WiFi to connected state
+ mWifiConnectivityManager.handleConnectionStateChanged(
+ WifiConnectivityManager.WIFI_STATE_CONNECTED);
+
+ // Set screen to on
+ mWifiConnectivityManager.handleScreenStateChanged(true);
+
+ verify(mWifiStateMachine, times(0)).startRoamToNetwork(anyInt(), anyObject());
+ }
+
+ /**
+ * Dump local log buffer.
+ *
+ * Expected behavior: Logs dumped from WifiConnectivityManager.dump()
+ * contain the message we put in mLocalLog.
+ */
+ @Test
+ public void dumpLocalLog() {
+ final String localLogMessage = "This is a message from the test";
+ mLocalLog.log(localLogMessage);
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mWifiConnectivityManager.dump(new FileDescriptor(), pw, new String[]{});
+ assertTrue(sw.toString().contains(localLogMessage));
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
index c187faf..c7b6180 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiControllerTest.java
@@ -27,12 +27,14 @@
import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import android.content.ContentResolver;
import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.WorkSource;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -42,6 +44,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -49,6 +52,7 @@
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
+import java.util.List;
/**
* Test WifiController for changes in and out of ECM and SoftAP modes.
@@ -78,13 +82,18 @@
when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true);
}
- MockLooper mLooper;
+ TestLooper mLooper;
@Mock Context mContext;
@Mock WifiServiceImpl mService;
@Mock FrameworkFacade mFacade;
@Mock WifiSettingsStore mSettingsStore;
@Mock WifiStateMachine mWifiStateMachine;
@Mock WifiLockManager mWifiLockManager;
+ @Mock ContentResolver mContentResolver;
+
+ ContentObserver mStayAwakeObserver;
+ ContentObserver mWifiIdleTimeObserver;
+ ContentObserver mWifiSleepPolicyObserver;
WifiController mWifiController;
@@ -92,14 +101,23 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mLooper = new MockLooper();
+ mLooper = new TestLooper();
initializeSettingsStore();
- when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ ArgumentCaptor<ContentObserver> observerCaptor =
+ ArgumentCaptor.forClass(ContentObserver.class);
mWifiController = new WifiController(mContext, mWifiStateMachine,
mSettingsStore, mWifiLockManager, mLooper.getLooper(), mFacade);
+ verify(mFacade, times(3)).registerContentObserver(eq(mContext), any(Uri.class), eq(false),
+ observerCaptor.capture());
+
+ List<ContentObserver> observers = observerCaptor.getAllValues();
+ mStayAwakeObserver = observers.get(0);
+ mWifiIdleTimeObserver = observers.get(1);
+ mWifiSleepPolicyObserver = observers.get(2);
mWifiController.start();
mLooper.dispatchAll();
@@ -267,7 +285,6 @@
InOrder inOrder = inOrder(mWifiStateMachine);
inOrder.verify(mWifiStateMachine).setSupplicantRunning(true);
inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE);
- inOrder.verify(mWifiStateMachine).setDriverStart(true);
assertEquals("DeviceActiveState", getCurrentState().getName());
}
@@ -295,7 +312,6 @@
InOrder inOrder = inOrder(mWifiStateMachine);
inOrder.verify(mWifiStateMachine).setSupplicantRunning(true);
inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE);
- inOrder.verify(mWifiStateMachine).setDriverStart(true);
assertEquals("DeviceActiveState", getCurrentState().getName());
}
@@ -329,7 +345,6 @@
InOrder inOrder = inOrder(mWifiStateMachine);
inOrder.verify(mWifiStateMachine).setSupplicantRunning(true);
inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE);
- inOrder.verify(mWifiStateMachine).setDriverStart(true);
assertEquals("FullLockHeldState", getCurrentState().getName());
}
@@ -404,7 +419,6 @@
inOrder.verify(mWifiStateMachine).setSupplicantRunning(false);
inOrder.verify(mWifiStateMachine).setSupplicantRunning(true);
inOrder.verify(mWifiStateMachine).setOperationalMode(WifiStateMachine.CONNECT_MODE);
- inOrder.verify(mWifiStateMachine).setDriverStart(true);
assertEquals("DeviceActiveState", getCurrentState().getName());
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
index faa2f71..33aab60 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
@@ -18,7 +18,6 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -39,7 +38,6 @@
private static final String TAG = "WifiCountryCodeTest";
private String mDefaultCountryCode = "US";
private String mTelephonyCountryCode = "JP";
- private String mPersistCountryCode = "";
private boolean mRevertCountryCodeOnCellularLoss = true;
@Mock WifiNative mWifiNative;
private WifiCountryCode mWifiCountryCode;
@@ -56,7 +54,6 @@
mWifiCountryCode = new WifiCountryCode(
mWifiNative,
mDefaultCountryCode,
- mPersistCountryCode,
mRevertCountryCodeOnCellularLoss);
}
@@ -80,7 +77,7 @@
*/
@Test
public void useTelephonyCountryCode() throws Exception {
- mWifiCountryCode.setCountryCode(mTelephonyCountryCode, false);
+ mWifiCountryCode.setCountryCode(mTelephonyCountryCode);
assertEquals(null, mWifiCountryCode.getCountryCodeSentToDriver());
// Supplicant started.
mWifiCountryCode.setReadyForChange(true);
@@ -100,7 +97,7 @@
mWifiCountryCode.setReadyForChange(true);
assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver());
// Telephony country code arrives.
- mWifiCountryCode.setCountryCode(mTelephonyCountryCode, false);
+ mWifiCountryCode.setCountryCode(mTelephonyCountryCode);
// Wifi get L2 connected.
mWifiCountryCode.setReadyForChange(false);
verify(mWifiNative, times(2)).setCountryCode(anyString());
@@ -118,7 +115,7 @@
// Wifi get L2 connected.
mWifiCountryCode.setReadyForChange(false);
// Telephony country code arrives.
- mWifiCountryCode.setCountryCode(mTelephonyCountryCode, false);
+ mWifiCountryCode.setCountryCode(mTelephonyCountryCode);
// Telephony coutry code won't be applied at this time.
assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCodeSentToDriver());
mWifiCountryCode.setReadyForChange(true);
@@ -133,7 +130,7 @@
*/
@Test
public void resetCountryCodeWhenSIMCardRemoved() throws Exception {
- mWifiCountryCode.setCountryCode(mTelephonyCountryCode, false);
+ mWifiCountryCode.setCountryCode(mTelephonyCountryCode);
// Supplicant started.
mWifiCountryCode.setReadyForChange(true);
// Wifi get L2 connected.
@@ -155,7 +152,7 @@
*/
@Test
public void resetCountryCodeWhenAirplaneModeEnabled() throws Exception {
- mWifiCountryCode.setCountryCode(mTelephonyCountryCode, false);
+ mWifiCountryCode.setCountryCode(mTelephonyCountryCode);
// Supplicant started.
mWifiCountryCode.setReadyForChange(true);
// Wifi get L2 connected.
@@ -172,22 +169,19 @@
}
/**
- * Test if we will set the persistent country code if it is not empty.
+ * Test if we can reset to the default country code when phone is out of service.
+ * Telephony service calls |setCountryCode| with an empty string when phone is out of service.
+ * In this case we should fall back to the default country code.
* @throws Exception
*/
@Test
- public void usePersistentCountryCode() throws Exception {
- String persistentCountryCode = "CH";
- mWifiCountryCode = new WifiCountryCode(
- mWifiNative,
- mDefaultCountryCode,
- persistentCountryCode,
- mRevertCountryCodeOnCellularLoss);
- // Supplicant started.
- mWifiCountryCode.setReadyForChange(true);
- // Wifi get L2 connected.
- mWifiCountryCode.setReadyForChange(false);
- verify(mWifiNative).setCountryCode(anyString());
- assertEquals(persistentCountryCode, mWifiCountryCode.getCountryCodeSentToDriver());
+ public void resetCountryCodeWhenOutOfService() throws Exception {
+ assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCode());
+ mWifiCountryCode.setCountryCode(mTelephonyCountryCode);
+ assertEquals(mTelephonyCountryCode, mWifiCountryCode.getCountryCode());
+ // Out of service.
+ mWifiCountryCode.setCountryCode("");
+ assertEquals(mDefaultCountryCode, mWifiCountryCode.getCountryCode());
}
+
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java
similarity index 68%
rename from tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java
rename to tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java
index d915ff3..f4b710e 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiLoggerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java
@@ -16,15 +16,12 @@
package com.android.server.wifi;
-import android.content.Context;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.internal.R;
-import android.util.LocalLog;
-
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.contains;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyObject;
import static org.mockito.Mockito.eq;
@@ -33,36 +30,45 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.content.Context;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.R;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import java.io.ByteArrayInputStream;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.regex.Pattern;
/**
- * Unit tests for {@link com.android.server.wifi.WifiLogger}.
+ * Unit tests for {@link WifiDiagnostics}.
*/
@SmallTest
-public class WifiLoggerTest {
- public static final String TAG = "WifiLoggerTest";
-
+public class WifiDiagnosticsTest {
@Mock WifiStateMachine mWsm;
@Mock WifiNative mWifiNative;
@Mock BuildProperties mBuildProperties;
@Mock Context mContext;
- WifiLogger mWifiLogger;
+ @Mock WifiInjector mWifiInjector;
+ @Spy FakeWifiLog mLog;
+ @Mock LastMileLogger mLastMileLogger;
+ @Mock Runtime mJavaRuntime;
+ @Mock Process mExternalProcess;
+ WifiDiagnostics mWifiDiagnostics;
private static final String FAKE_RING_BUFFER_NAME = "fake-ring-buffer";
private static final int SMALL_RING_BUFFER_SIZE_KB = 32;
private static final int LARGE_RING_BUFFER_SIZE_KB = 1024;
private static final int BYTES_PER_KBYTE = 1024;
- private LocalLog mWifiNativeLocalLog;
+ private static final long FAKE_CONNECTION_ID = 1;
private WifiNative.RingBufferStatus mFakeRbs;
/**
@@ -71,7 +77,7 @@
* dimension is the byte index within that record.
*/
private final byte[][] getLoggerRingBufferData() throws Exception {
- return mWifiLogger.getBugReports().get(0).ringBuffers.get(FAKE_RING_BUFFER_NAME);
+ return mWifiDiagnostics.getBugReports().get(0).ringBuffers.get(FAKE_RING_BUFFER_NAME);
}
/**
@@ -86,14 +92,15 @@
WifiNative.RingBufferStatus[] ringBufferStatuses = new WifiNative.RingBufferStatus[] {
mFakeRbs
};
- mWifiNativeLocalLog = new LocalLog(8192);
when(mWifiNative.getRingBufferStatus()).thenReturn(ringBufferStatuses);
when(mWifiNative.readKernelLog()).thenReturn("");
- when(mWifiNative.getLocalLog()).thenReturn(mWifiNativeLocalLog);
when(mBuildProperties.isEngBuild()).thenReturn(false);
when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
when(mBuildProperties.isUserBuild()).thenReturn(true);
+ when(mExternalProcess.getInputStream()).thenReturn(new ByteArrayInputStream(new byte[0]));
+ when(mExternalProcess.getErrorStream()).thenReturn(new ByteArrayInputStream(new byte[0]));
+ when(mJavaRuntime.exec(anyString())).thenReturn(mExternalProcess);
MockResources resources = new MockResources();
resources.setInteger(R.integer.config_wifi_logger_ring_buffer_default_size_limit_kb,
@@ -101,8 +108,11 @@
resources.setInteger(R.integer.config_wifi_logger_ring_buffer_verbose_size_limit_kb,
LARGE_RING_BUFFER_SIZE_KB);
when(mContext.getResources()).thenReturn(resources);
+ when(mWifiInjector.makeLog(anyString())).thenReturn(mLog);
+ when(mWifiInjector.getJavaRuntime()).thenReturn(mJavaRuntime);
- mWifiLogger = new WifiLogger(mContext, mWsm, mWifiNative, mBuildProperties);
+ mWifiDiagnostics = new WifiDiagnostics(
+ mContext, mWifiInjector, mWsm, mWifiNative, mBuildProperties, mLastMileLogger);
mWifiNative.enableVerboseLogging(0);
}
@@ -110,7 +120,7 @@
@Test
public void startLoggingRegistersLogEventHandler() throws Exception {
final boolean verbosityToggle = false; // even default mode wants log events from HAL
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
verify(mWifiNative).setLoggingEventHandler(anyObject());
}
@@ -124,12 +134,12 @@
final boolean verbosityToggle = false; // even default mode wants log events from HAL
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(false);
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
verify(mWifiNative).setLoggingEventHandler(anyObject());
reset(mWifiNative);
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
verify(mWifiNative).setLoggingEventHandler(anyObject());
}
@@ -140,11 +150,11 @@
final boolean verbosityToggle = false; // even default mode wants log events from HAL
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
verify(mWifiNative).setLoggingEventHandler(anyObject());
reset(mWifiNative);
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
verify(mWifiNative, never()).setLoggingEventHandler(anyObject());
}
@@ -158,25 +168,34 @@
@Test
public void startLoggingStopsAndRestartsRingBufferLogging() throws Exception {
final boolean verbosityToggle = false;
- mWifiLogger.startLogging(verbosityToggle);
+ setBuildPropertiesToEnableRingBuffers();
+ mWifiDiagnostics.startLogging(verbosityToggle);
verify(mWifiNative).startLoggingRingBuffer(
- eq(WifiLogger.VERBOSE_NO_LOG), anyInt(), anyInt(), anyInt(),
+ eq(WifiDiagnostics.VERBOSE_NO_LOG), anyInt(), anyInt(), anyInt(),
eq(FAKE_RING_BUFFER_NAME));
verify(mWifiNative).startLoggingRingBuffer(
- eq(WifiLogger.VERBOSE_NORMAL_LOG), anyInt(), anyInt(), anyInt(),
+ eq(WifiDiagnostics.VERBOSE_NORMAL_LOG), anyInt(), anyInt(), anyInt(),
eq(FAKE_RING_BUFFER_NAME));
}
+ @Test
+ public void startLoggingDoesNotStartRingBuffersOnUserBuilds() throws Exception {
+ final boolean verbosityToggle = true;
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ verify(mWifiNative, never()).startLoggingRingBuffer(
+ anyInt(), anyInt(), anyInt(), anyInt(), anyString());
+ }
+
/** Verifies that, if a log handler was registered, then stopLogging() resets it. */
@Test
public void stopLoggingResetsLogHandlerIfHandlerWasRegistered() throws Exception {
final boolean verbosityToggle = false; // even default mode wants log events from HAL
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
reset(mWifiNative);
- mWifiLogger.stopLogging();
+ mWifiDiagnostics.stopLogging();
verify(mWifiNative).resetLogHandler();
}
@@ -184,7 +203,7 @@
@Test
public void stopLoggingOnlyResetsLogHandlerIfHandlerWasRegistered() throws Exception {
final boolean verbosityToggle = false; // even default mode wants log events from HAL
- mWifiLogger.stopLogging();
+ mWifiDiagnostics.stopLogging();
verify(mWifiNative, never()).resetLogHandler();
}
@@ -194,15 +213,15 @@
final boolean verbosityToggle = false; // even default mode wants log events from HAL
when(mWifiNative.setLoggingEventHandler(anyObject())).thenReturn(true);
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
reset(mWifiNative);
when(mWifiNative.resetLogHandler()).thenReturn(true);
- mWifiLogger.stopLogging();
+ mWifiDiagnostics.stopLogging();
verify(mWifiNative).resetLogHandler();
reset(mWifiNative);
- mWifiLogger.stopLogging();
+ mWifiDiagnostics.stopLogging();
verify(mWifiNative, never()).resetLogHandler();
}
@@ -212,11 +231,12 @@
@Test
public void canCaptureAndStoreRingBufferData() throws Exception {
final boolean verbosityToggle = false;
- mWifiLogger.startLogging(verbosityToggle);
+ setBuildPropertiesToEnableRingBuffers();
+ mWifiDiagnostics.startLogging(verbosityToggle);
final byte[] data = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
- mWifiLogger.onRingBufferData(mFakeRbs, data);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.onRingBufferData(mFakeRbs, data);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
byte[][] ringBufferData = getLoggerRingBufferData();
assertEquals(1, ringBufferData.length);
@@ -229,13 +249,14 @@
@Test
public void loggerDiscardsExtraneousData() throws Exception {
final boolean verbosityToggle = false;
- mWifiLogger.startLogging(verbosityToggle);
+ setBuildPropertiesToEnableRingBuffers();
+ mWifiDiagnostics.startLogging(verbosityToggle);
final byte[] data1 = new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE];
final byte[] data2 = {1, 2, 3};
- mWifiLogger.onRingBufferData(mFakeRbs, data1);
- mWifiLogger.onRingBufferData(mFakeRbs, data2);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.onRingBufferData(mFakeRbs, data1);
+ mWifiDiagnostics.onRingBufferData(mFakeRbs, data2);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
byte[][] ringBufferData = getLoggerRingBufferData();
assertEquals(1, ringBufferData.length);
@@ -249,7 +270,7 @@
@Test
public void startLoggingStartsPacketFateWithoutVerboseMode() {
final boolean verbosityToggle = false;
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
verify(mWifiNative).startPktFateMonitoring();
}
@@ -260,19 +281,29 @@
@Test
public void startLoggingStartsPacketFateInVerboseMode() {
final boolean verbosityToggle = true;
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
verify(mWifiNative).startPktFateMonitoring();
}
+ // Verifies that startLogging() reports failure of startPktFateMonitoring().
+ @Test
+ public void startLoggingReportsFailureOfStartPktFateMonitoring() {
+ final boolean verbosityToggle = true;
+ when(mWifiNative.startPktFateMonitoring()).thenReturn(false);
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ verify(mLog).wC(contains("Failed"));
+ }
+
/**
- * Verifies that, when verbose mode is not enabled, reportConnectionFailure() still
- * fetches packet fates.
+ * Verifies that, when verbose mode is not enabled,
+ * reportConnectionEvent(WifiDiagnostics.CONNECTION_EVENT_FAILED) still fetches packet fates.
*/
@Test
public void reportConnectionFailureIsIgnoredWithoutVerboseMode() {
final boolean verbosityToggle = false;
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.reportConnectionFailure();
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(anyObject());
verify(mWifiNative).getRxPktFates(anyObject());
}
@@ -283,12 +314,43 @@
@Test
public void reportConnectionFailureFetchesFatesInVerboseMode() {
final boolean verbosityToggle = true;
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.reportConnectionFailure();
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(anyObject());
verify(mWifiNative).getRxPktFates(anyObject());
}
+ @Test
+ public void reportConnectionEventPropagatesStartToLastMileLogger() {
+ final boolean verbosityToggle = false;
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_STARTED);
+ verify(mLastMileLogger).reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_STARTED);
+ }
+
+ @Test
+ public void reportConnectionEventPropagatesSuccessToLastMileLogger() {
+ final boolean verbosityToggle = false;
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
+ verify(mLastMileLogger).reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_SUCCEEDED);
+ }
+
+ @Test
+ public void reportConnectionEventPropagatesFailureToLastMileLogger() {
+ final boolean verbosityToggle = false;
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
+ verify(mLastMileLogger).reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
+ }
+
/**
* Verifies that we try to fetch TX fates, even if fetching RX fates failed.
*/
@@ -296,8 +358,9 @@
public void loggerFetchesTxFatesEvenIfFetchingRxFatesFails() {
final boolean verbosityToggle = true;
when(mWifiNative.getRxPktFates(anyObject())).thenReturn(false);
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.reportConnectionFailure();
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(anyObject());
verify(mWifiNative).getRxPktFates(anyObject());
}
@@ -309,8 +372,9 @@
public void loggerFetchesRxFatesEvenIfFetchingTxFatesFails() {
final boolean verbosityToggle = true;
when(mWifiNative.getTxPktFates(anyObject())).thenReturn(false);
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.reportConnectionFailure();
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(anyObject());
verify(mWifiNative).getRxPktFates(anyObject());
}
@@ -321,8 +385,8 @@
final boolean verbosityToggle = false;
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
verify(mWifiNative).getTxPktFates(anyObject());
verify(mWifiNative).getRxPktFates(anyObject());
}
@@ -335,7 +399,7 @@
public void dumpSucceedsWhenNoFatesHaveNotBeenFetched() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
String fateDumpString = sw.toString();
assertTrue(fateDumpString.contains("Last failed"));
@@ -351,14 +415,15 @@
@Test
public void dumpSucceedsWhenFatesHaveBeenFetchedButAreEmpty() {
final boolean verbosityToggle = true;
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.reportConnectionFailure();
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(anyObject());
verify(mWifiNative).getRxPktFates(anyObject());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
String fateDumpString = sw.toString();
assertTrue(fateDumpString.contains("Last failed"));
@@ -368,7 +433,7 @@
}
private String getDumpString(boolean verbose) {
- mWifiLogger.startLogging(verbose);
+ mWifiDiagnostics.startLogging(verbose);
mWifiNative.enableVerboseLogging(verbose ? 1 : 0);
when(mWifiNative.getTxPktFates(anyObject())).then(new AnswerWithArguments() {
public boolean answer(WifiNative.TxFateReport[] fates) {
@@ -396,11 +461,12 @@
return true;
}
});
- mWifiLogger.reportConnectionFailure();
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
return sw.toString();
}
@@ -502,7 +568,7 @@
@Test
public void dumpOmitsFatesIfVerboseIsDisabledAfterFetch() {
final boolean verbosityToggle = true;
- mWifiLogger.startLogging(verbosityToggle);
+ mWifiDiagnostics.startLogging(verbosityToggle);
when(mWifiNative.getTxPktFates(anyObject())).then(new AnswerWithArguments() {
public boolean answer(WifiNative.TxFateReport[] fates) {
fates[0] = new WifiNative.TxFateReport(
@@ -521,31 +587,33 @@
return true;
}
});
- mWifiLogger.reportConnectionFailure();
+ mWifiDiagnostics.reportConnectionEvent(
+ FAKE_CONNECTION_ID, WifiDiagnostics.CONNECTION_EVENT_FAILED);
verify(mWifiNative).getTxPktFates(anyObject());
verify(mWifiNative).getRxPktFates(anyObject());
final boolean newVerbosityToggle = false;
- mWifiLogger.startLogging(newVerbosityToggle);
+ mWifiDiagnostics.startLogging(newVerbosityToggle);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
String fateDumpString = sw.toString();
assertFalse(fateDumpString.contains("VERBOSE PACKET FATE DUMP"));
assertFalse(fateDumpString.contains("Frame bytes"));
}
- /** Verifies that the default size of our ring buffers is small. */
@Test
- public void ringBufferSizeIsSmallByDefault() throws Exception {
- final boolean verbosityToggle = false;
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.onRingBufferData(
- mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
- assertEquals(0, getLoggerRingBufferData().length);
+ public void dumpSucceedsEvenIfRingBuffersAreDisabled() {
+ final boolean verbosityToggle = true;
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ verify(mWifiNative, never()).startLoggingRingBuffer(
+ anyInt(), anyInt(), anyInt(), anyInt(), anyString());
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{"bogus", "args"});
}
/** Verifies that we use small ring buffers by default, on userdebug builds. */
@@ -555,10 +623,10 @@
when(mBuildProperties.isUserdebugBuild()).thenReturn(true);
when(mBuildProperties.isEngBuild()).thenReturn(false);
when(mBuildProperties.isUserBuild()).thenReturn(false);
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.onRingBufferData(
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
assertEquals(0, getLoggerRingBufferData().length);
}
@@ -569,10 +637,10 @@
when(mBuildProperties.isEngBuild()).thenReturn(true);
when(mBuildProperties.isUserdebugBuild()).thenReturn(false);
when(mBuildProperties.isUserBuild()).thenReturn(false);
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.onRingBufferData(
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
assertEquals(0, getLoggerRingBufferData().length);
}
@@ -580,47 +648,53 @@
@Test
public void ringBufferSizeIsLargeInVerboseMode() throws Exception {
final boolean verbosityToggle = true;
- mWifiLogger.startLogging(verbosityToggle);
- mWifiLogger.onRingBufferData(
+ setBuildPropertiesToEnableRingBuffers();
+
+ mWifiDiagnostics.startLogging(verbosityToggle);
+ mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[LARGE_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
assertEquals(1, getLoggerRingBufferData().length);
}
/** Verifies that we use large ring buffers when switched from normal to verbose mode. */
@Test
public void startLoggingGrowsRingBuffersIfNeeded() throws Exception {
- mWifiLogger.startLogging(false /* verbose disabled */);
- mWifiLogger.startLogging(true /* verbose enabled */);
- mWifiLogger.onRingBufferData(
+ setBuildPropertiesToEnableRingBuffers();
+
+ mWifiDiagnostics.startLogging(false /* verbose disabled */);
+ mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[LARGE_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE]);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
assertEquals(1, getLoggerRingBufferData().length);
}
/** Verifies that we use small ring buffers when switched from verbose to normal mode. */
@Test
public void startLoggingShrinksRingBuffersIfNeeded() throws Exception {
- mWifiLogger.startLogging(true /* verbose enabled */);
- mWifiLogger.onRingBufferData(
+ setBuildPropertiesToEnableRingBuffers();
+
+ mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
// Existing data is nuked (too large).
- mWifiLogger.startLogging(false /* verbose disabled */);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.startLogging(false /* verbose disabled */);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
assertEquals(0, getLoggerRingBufferData().length);
// New data must obey limit as well.
- mWifiLogger.onRingBufferData(
+ mWifiDiagnostics.onRingBufferData(
mFakeRbs, new byte[SMALL_RING_BUFFER_SIZE_KB * BYTES_PER_KBYTE + 1]);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
assertEquals(0, getLoggerRingBufferData().length);
}
/** Verifies that we skip the firmware and driver dumps if verbose is not enabled. */
@Test
public void captureBugReportSkipsFirmwareAndDriverDumpsByDefault() {
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative, never()).getFwMemoryDump();
verify(mWifiNative, never()).getDriverStateDump();
}
@@ -628,8 +702,8 @@
/** Verifies that we capture the firmware and driver dumps if verbose is enabled. */
@Test
public void captureBugReportTakesFirmwareAndDriverDumpsInVerboseMode() {
- mWifiLogger.startLogging(true /* verbose enabled */);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getFwMemoryDump();
verify(mWifiNative).getDriverStateDump();
}
@@ -639,27 +713,27 @@
public void dumpIncludesDriverStateDumpIfAvailable() {
when(mWifiNative.getDriverStateDump()).thenReturn(new byte[]{0, 1, 2});
- mWifiLogger.startLogging(true /* verbose enabled */);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getDriverStateDump();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{});
- assertTrue(sw.toString().contains(WifiLogger.DRIVER_DUMP_SECTION_HEADER));
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
+ assertTrue(sw.toString().contains(WifiDiagnostics.DRIVER_DUMP_SECTION_HEADER));
}
/** Verifies that the dump skips driver state, if driver state was not provided by HAL. */
@Test
public void dumpOmitsDriverStateDumpIfUnavailable() {
- mWifiLogger.startLogging(true /* verbose enabled */);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getDriverStateDump();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{});
- assertFalse(sw.toString().contains(WifiLogger.DRIVER_DUMP_SECTION_HEADER));
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
+ assertFalse(sw.toString().contains(WifiDiagnostics.DRIVER_DUMP_SECTION_HEADER));
}
/** Verifies that the dump omits driver state, if verbose was disabled after capture. */
@@ -667,16 +741,16 @@
public void dumpOmitsDriverStateDumpIfVerboseDisabledAfterCapture() {
when(mWifiNative.getDriverStateDump()).thenReturn(new byte[]{0, 1, 2});
- mWifiLogger.startLogging(true /* verbose enabled */);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getDriverStateDump();
- mWifiLogger.startLogging(false /* verbose no longer enabled */);
+ mWifiDiagnostics.startLogging(false /* verbose no longer enabled */);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{});
- assertFalse(sw.toString().contains(WifiLogger.DRIVER_DUMP_SECTION_HEADER));
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
+ assertFalse(sw.toString().contains(WifiDiagnostics.DRIVER_DUMP_SECTION_HEADER));
}
/** Verifies that the dump includes firmware dump, if firmware dump was provided by HAL. */
@@ -684,27 +758,27 @@
public void dumpIncludesFirmwareMemoryDumpIfAvailable() {
when(mWifiNative.getFwMemoryDump()).thenReturn(new byte[]{0, 1, 2});
- mWifiLogger.startLogging(true /* verbose enabled */);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getFwMemoryDump();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{});
- assertTrue(sw.toString().contains(WifiLogger.FIRMWARE_DUMP_SECTION_HEADER));
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
+ assertTrue(sw.toString().contains(WifiDiagnostics.FIRMWARE_DUMP_SECTION_HEADER));
}
/** Verifies that the dump skips firmware memory, if firmware memory was not provided by HAL. */
@Test
public void dumpOmitsFirmwareMemoryDumpIfUnavailable() {
- mWifiLogger.startLogging(true /* verbose enabled */);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getFwMemoryDump();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{});
- assertFalse(sw.toString().contains(WifiLogger.FIRMWARE_DUMP_SECTION_HEADER));
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
+ assertFalse(sw.toString().contains(WifiDiagnostics.FIRMWARE_DUMP_SECTION_HEADER));
}
/** Verifies that the dump omits firmware memory, if verbose was disabled after capture. */
@@ -712,28 +786,28 @@
public void dumpOmitsFirmwareMemoryDumpIfVerboseDisabledAfterCapture() {
when(mWifiNative.getFwMemoryDump()).thenReturn(new byte[]{0, 1, 2});
- mWifiLogger.startLogging(true /* verbose enabled */);
- mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_NONE);
+ mWifiDiagnostics.startLogging(true /* verbose enabled */);
+ mWifiDiagnostics.captureBugReportData(WifiDiagnostics.REPORT_REASON_NONE);
verify(mWifiNative).getFwMemoryDump();
- mWifiLogger.startLogging(false /* verbose no longer enabled */);
+ mWifiDiagnostics.startLogging(false /* verbose no longer enabled */);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{});
- assertFalse(sw.toString().contains(WifiLogger.FIRMWARE_DUMP_SECTION_HEADER));
+ mWifiDiagnostics.dump(new FileDescriptor(), pw, new String[]{});
+ assertFalse(sw.toString().contains(WifiDiagnostics.FIRMWARE_DUMP_SECTION_HEADER));
}
- /** Verifies that the dump() includes contents of WifiNative's LocalLog. */
@Test
- public void dumpIncludesContentOfWifiNativeLocalLog() {
- final String wifiNativeLogMessage = "This is a message";
- mWifiNativeLocalLog.log(wifiNativeLogMessage);
+ public void dumpRequestsLastMileLoggerDump() {
+ mWifiDiagnostics.dump(
+ new FileDescriptor(), new PrintWriter(new StringWriter()), new String[]{});
+ verify(mLastMileLogger).dump(anyObject());
+ }
- mWifiLogger.startLogging(false /* verbose disabled */);
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- mWifiLogger.dump(new FileDescriptor(), pw, new String[]{});
- assertTrue(sw.toString().contains(wifiNativeLogMessage));
+ private void setBuildPropertiesToEnableRingBuffers() {
+ when(mBuildProperties.isEngBuild()).thenReturn(false);
+ when(mBuildProperties.isUserdebugBuild()).thenReturn(true);
+ when(mBuildProperties.isUserBuild()).thenReturn(false);
}
}
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 08163e7..0ecd53d 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiLastResortWatchdogTest.java
@@ -41,7 +41,7 @@
public class WifiLastResortWatchdogTest {
WifiLastResortWatchdog mLastResortWatchdog;
@Mock WifiMetrics mWifiMetrics;
- @Mock WifiController mWifiController;
+ @Mock SelfRecovery mSelfRecovery;
private String[] mSsids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""};
private String[] mBssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55",
"c0:ff:ee:ee:e3:ee"};
@@ -55,8 +55,7 @@
@Before
public void setUp() throws Exception {
initMocks(this);
- mLastResortWatchdog = new WifiLastResortWatchdog(mWifiMetrics);
- mLastResortWatchdog.setWifiController(mWifiController);
+ mLastResortWatchdog = new WifiLastResortWatchdog(mSelfRecovery, mWifiMetrics);
}
private List<Pair<ScanDetail, WifiConfiguration>> createFilteredQnsCandidates(String[] ssids,
@@ -1280,8 +1279,8 @@
ssids[ssids.length - 1], bssids[ssids.length - 1],
WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
assertEquals(true, watchdogTriggered);
- verify(mWifiController).sendMessage(WifiController.CMD_RESTART_WIFI);
- reset(mWifiController);
+ verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_LAST_RESORT_WATCHDOG));
+ reset(mSelfRecovery);
}
/**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
index 1bbdda9..d5d79b8 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java
@@ -18,7 +18,6 @@
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.*;
-import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import android.content.Context;
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
index 15a5327..1c09cac 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java
@@ -21,11 +21,19 @@
import android.net.NetworkAgent;
import android.net.wifi.ScanResult;
+import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
+
import com.android.server.wifi.hotspot2.NetworkDetail;
+import com.android.server.wifi.nano.WifiMetricsProto;
+import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
import org.junit.Before;
import org.junit.Test;
@@ -33,8 +41,11 @@
import org.mockito.MockitoAnnotations;
import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -47,14 +58,16 @@
WifiMetrics mWifiMetrics;
WifiMetricsProto.WifiLog mDeserializedWifiMetrics;
+ TestLooper mTestLooper;
@Mock Clock mClock;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mDeserializedWifiMetrics = null;
- when(mClock.elapsedRealtime()).thenReturn((long) 0);
- mWifiMetrics = new WifiMetrics(mClock);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 0);
+ mTestLooper = new TestLooper();
+ mWifiMetrics = new WifiMetrics(mClock, mTestLooper.getLooper());
}
/**
@@ -92,7 +105,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);
@@ -116,7 +129,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, WifiMetrics.CLEAN_DUMP_ARG};
mWifiMetrics.dump(null, writer, args);
@@ -161,16 +174,18 @@
assertDeserializedMetricsCorrect();
}
- private static final int NUM_SAVED_NETWORKS = 1;
private static final int NUM_OPEN_NETWORKS = 2;
private static final int NUM_PERSONAL_NETWORKS = 3;
private static final int NUM_ENTERPRISE_NETWORKS = 5;
- private static final int NUM_HIDDEN_NETWORKS = 3;
- private static final int NUM_PASSPOINT_NETWORKS = 4;
+ private static final int NUM_SAVED_NETWORKS = NUM_OPEN_NETWORKS + NUM_PERSONAL_NETWORKS
+ + NUM_ENTERPRISE_NETWORKS;
+ private static final int NUM_HIDDEN_NETWORKS = NUM_OPEN_NETWORKS;
+ private static final int NUM_PASSPOINT_NETWORKS = NUM_ENTERPRISE_NETWORKS;
+ private static final int NUM_NETWORKS_ADDED_BY_USER = 1;
+ private static final int NUM_NETWORKS_ADDED_BY_APPS = NUM_SAVED_NETWORKS
+ - NUM_NETWORKS_ADDED_BY_USER;
private static final boolean TEST_VAL_IS_LOCATION_ENABLED = true;
private static final boolean IS_SCANNING_ALWAYS_ENABLED = true;
- private static final int NUM_NEWTORKS_ADDED_BY_USER = 13;
- private static final int NUM_NEWTORKS_ADDED_BY_APPS = 17;
private static final int NUM_EMPTY_SCAN_RESULTS = 19;
private static final int NUM_NON_EMPTY_SCAN_RESULTS = 23;
private static final int NUM_SCAN_UNKNOWN = 1;
@@ -212,6 +227,14 @@
private static final int NUM_WIFI_SCORES_TO_INCREMENT = 20;
private static final int WIFI_SCORE_RANGE_MAX = 60;
private static final int NUM_OUT_OF_BOUND_ENTRIES = 10;
+ private static final int MAX_NUM_SOFTAP_RETURN_CODES = 3;
+ private static final int NUM_SOFTAP_START_SUCCESS = 3;
+ private static final int NUM_SOFTAP_FAILED_GENERAL_ERROR = 2;
+ private static final int NUM_SOFTAP_FAILED_NO_CHANNEL = 1;
+ private static final int NUM_HAL_CRASHES = 11;
+ private static final int NUM_WIFICOND_CRASHES = 12;
+ private static final int NUM_WIFI_ON_FAILURE_DUE_TO_HAL = 13;
+ private static final int NUM_WIFI_ON_FAILURE_DUE_TO_WIFICOND = 14;
private ScanDetail buildMockScanDetail(boolean hidden, NetworkDetail.HSRelease hSRelease,
String capabilities) {
@@ -242,18 +265,27 @@
return mockScanDetails;
}
+ private List<WifiConfiguration> buildSavedNetworkList() {
+ List<WifiConfiguration> testSavedNetworks = new ArrayList<WifiConfiguration>();
+ for (int i = 0; i < NUM_OPEN_NETWORKS; i++) {
+ testSavedNetworks.add(WifiConfigurationTestUtil.createOpenHiddenNetwork());
+ }
+ for (int i = 0; i < NUM_PERSONAL_NETWORKS; i++) {
+ testSavedNetworks.add(WifiConfigurationTestUtil.createPskNetwork());
+ }
+ for (int i = 0; i < NUM_ENTERPRISE_NETWORKS; i++) {
+ // Passpoint networks are counted in both Passpoint and Enterprise counters
+ testSavedNetworks.add(WifiConfigurationTestUtil.createPasspointNetwork());
+ }
+ testSavedNetworks.get(0).selfAdded = true;
+ return testSavedNetworks;
+ }
+
/**
* Set simple metrics, increment others
*/
public void setAndIncrementMetrics() throws Exception {
- mWifiMetrics.setNumSavedNetworks(NUM_SAVED_NETWORKS);
- mWifiMetrics.setNumOpenNetworks(NUM_OPEN_NETWORKS);
- mWifiMetrics.setNumPersonalNetworks(NUM_PERSONAL_NETWORKS);
- mWifiMetrics.setNumEnterpriseNetworks(NUM_ENTERPRISE_NETWORKS);
- mWifiMetrics.setNumHiddenNetworks(NUM_HIDDEN_NETWORKS);
- mWifiMetrics.setNumPasspointNetworks(NUM_PASSPOINT_NETWORKS);
- mWifiMetrics.setNumNetworksAddedByUser(NUM_NEWTORKS_ADDED_BY_USER);
- mWifiMetrics.setNumNetworksAddedByApps(NUM_NEWTORKS_ADDED_BY_APPS);
+ mWifiMetrics.updateSavedNetworks(buildSavedNetworkList());
mWifiMetrics.setIsLocationEnabled(TEST_VAL_IS_LOCATION_ENABLED);
mWifiMetrics.setIsScanningAlwaysEnabled(IS_SCANNING_ALWAYS_ENABLED);
@@ -363,6 +395,30 @@
for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MAX + i);
}
+
+ // increment soft ap start return codes
+ for (int i = 0; i < NUM_SOFTAP_START_SUCCESS; i++) {
+ mWifiMetrics.incrementSoftApStartResult(true, 0);
+ }
+ for (int i = 0; i < NUM_SOFTAP_FAILED_GENERAL_ERROR; i++) {
+ mWifiMetrics.incrementSoftApStartResult(false, WifiManager.SAP_START_FAILURE_GENERAL);
+ }
+ for (int i = 0; i < NUM_SOFTAP_FAILED_NO_CHANNEL; i++) {
+ mWifiMetrics.incrementSoftApStartResult(false,
+ WifiManager.SAP_START_FAILURE_NO_CHANNEL);
+ }
+ for (int i = 0; i < NUM_HAL_CRASHES; i++) {
+ mWifiMetrics.incrementNumHalCrashes();
+ }
+ for (int i = 0; i < NUM_WIFICOND_CRASHES; i++) {
+ mWifiMetrics.incrementNumWificondCrashes();
+ }
+ for (int i = 0; i < NUM_WIFI_ON_FAILURE_DUE_TO_HAL; i++) {
+ mWifiMetrics.incrementNumWifiOnFailureDueToHal();
+ }
+ for (int i = 0; i < NUM_WIFI_ON_FAILURE_DUE_TO_WIFICOND; i++) {
+ mWifiMetrics.incrementNumWifiOnFailureDueToWificond();
+ }
}
/**
@@ -379,13 +435,13 @@
+ "== NUM_ENTERPRISE_NETWORKS",
mDeserializedWifiMetrics.numEnterpriseNetworks, NUM_ENTERPRISE_NETWORKS);
assertEquals("mDeserializedWifiMetrics.numNetworksAddedByUser "
- + "== NUM_NEWTORKS_ADDED_BY_USER",
- mDeserializedWifiMetrics.numNetworksAddedByUser, NUM_NEWTORKS_ADDED_BY_USER);
+ + "== NUM_NETWORKS_ADDED_BY_USER",
+ mDeserializedWifiMetrics.numNetworksAddedByUser, NUM_NETWORKS_ADDED_BY_USER);
assertEquals(NUM_HIDDEN_NETWORKS, mDeserializedWifiMetrics.numHiddenNetworks);
assertEquals(NUM_PASSPOINT_NETWORKS, mDeserializedWifiMetrics.numPasspointNetworks);
assertEquals("mDeserializedWifiMetrics.numNetworksAddedByApps "
- + "== NUM_NEWTORKS_ADDED_BY_APPS",
- mDeserializedWifiMetrics.numNetworksAddedByApps, NUM_NEWTORKS_ADDED_BY_APPS);
+ + "== NUM_NETWORKS_ADDED_BY_APPS",
+ mDeserializedWifiMetrics.numNetworksAddedByApps, NUM_NETWORKS_ADDED_BY_APPS);
assertEquals("mDeserializedWifiMetrics.isLocationEnabled == TEST_VAL_IS_LOCATION_ENABLED",
mDeserializedWifiMetrics.isLocationEnabled, TEST_VAL_IS_LOCATION_ENABLED);
assertEquals("mDeserializedWifiMetrics.isScanningAlwaysEnabled "
@@ -487,6 +543,24 @@
sb_wifi_limits.append("Wifi Score limit is " + NetworkAgent.WIFI_BASE_SCORE
+ ">= " + WIFI_SCORE_RANGE_MAX);
assertTrue(sb_wifi_limits.toString(), NetworkAgent.WIFI_BASE_SCORE <= WIFI_SCORE_RANGE_MAX);
+ assertEquals(MAX_NUM_SOFTAP_RETURN_CODES, mDeserializedWifiMetrics.softApReturnCode.length);
+ assertEquals(WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY,
+ mDeserializedWifiMetrics.softApReturnCode[0].startResult);
+ assertEquals(NUM_SOFTAP_START_SUCCESS, mDeserializedWifiMetrics.softApReturnCode[0].count);
+ assertEquals(WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR,
+ mDeserializedWifiMetrics.softApReturnCode[1].startResult);
+ assertEquals(NUM_SOFTAP_FAILED_GENERAL_ERROR,
+ mDeserializedWifiMetrics.softApReturnCode[1].count);
+ assertEquals(WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL,
+ mDeserializedWifiMetrics.softApReturnCode[2].startResult);
+ assertEquals(NUM_SOFTAP_FAILED_NO_CHANNEL,
+ mDeserializedWifiMetrics.softApReturnCode[2].count);
+ assertEquals(NUM_HAL_CRASHES, mDeserializedWifiMetrics.numHalCrashes);
+ assertEquals(NUM_WIFICOND_CRASHES, mDeserializedWifiMetrics.numWificondCrashes);
+ assertEquals(NUM_WIFI_ON_FAILURE_DUE_TO_HAL,
+ mDeserializedWifiMetrics.numWifiOnFailureDueToHal);
+ assertEquals(NUM_WIFI_ON_FAILURE_DUE_TO_WIFICOND,
+ mDeserializedWifiMetrics.numWifiOnFailureDueToWificond);
}
/**
@@ -659,6 +733,362 @@
2, mDeserializedWifiMetrics.connectionEvent.length);
}
+ private static final int NUM_REPEATED_DELTAS = 7;
+ private static final int REPEATED_DELTA = 0;
+ private static final int SINGLE_GOOD_DELTA = 1;
+ private static final int SINGLE_TIMEOUT_DELTA = 2;
+ private static final int NUM_REPEATED_BOUND_DELTAS = 2;
+ private static final int MAX_DELTA_LEVEL = 127;
+ private static final int MIN_DELTA_LEVEL = -127;
+ private static final int ARBITRARY_DELTA_LEVEL = 20;
+
+ /**
+ * Sunny day RSSI delta logging scenario.
+ * Logs one rssi delta value multiple times
+ * Logs a different delta value a single time
+ */
+ @Test
+ public void testRssiDeltasSuccessfulLogging() throws Exception {
+ // Generate some repeated deltas
+ for (int i = 0; i < NUM_REPEATED_DELTAS; i++) {
+ generateRssiDelta(MIN_RSSI_LEVEL, REPEATED_DELTA,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
+ }
+ // Generate a single delta
+ generateRssiDelta(MIN_RSSI_LEVEL, SINGLE_GOOD_DELTA,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
+ dumpProtoAndDeserialize();
+ assertEquals(2, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ // Check the repeated deltas
+ assertEquals(NUM_REPEATED_DELTAS, mDeserializedWifiMetrics.rssiPollDeltaCount[0].count);
+ assertEquals(REPEATED_DELTA, mDeserializedWifiMetrics.rssiPollDeltaCount[0].rssi);
+ // Check the single delta
+ assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[1].count);
+ assertEquals(SINGLE_GOOD_DELTA, mDeserializedWifiMetrics.rssiPollDeltaCount[1].rssi);
+ }
+
+ /**
+ * Tests that Rssi Delta events whose scanResult and Rssi Poll come too far apart, timeout,
+ * and are not logged.
+ */
+ @Test
+ public void testRssiDeltasTimeout() throws Exception {
+ // Create timed out rssi deltas
+ generateRssiDelta(MIN_RSSI_LEVEL, REPEATED_DELTA,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS + 1);
+ generateRssiDelta(MIN_RSSI_LEVEL, SINGLE_TIMEOUT_DELTA,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS + 1);
+ dumpProtoAndDeserialize();
+ assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ }
+
+ /**
+ * Tests the exact inclusive boundaries of RSSI delta logging.
+ */
+ @Test
+ public void testRssiDeltaSuccessfulLoggingExactBounds() throws Exception {
+ generateRssiDelta(MIN_RSSI_LEVEL, MAX_DELTA_LEVEL,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
+ generateRssiDelta(MAX_RSSI_LEVEL, MIN_DELTA_LEVEL,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
+ dumpProtoAndDeserialize();
+ assertEquals(2, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ assertEquals(MIN_DELTA_LEVEL, mDeserializedWifiMetrics.rssiPollDeltaCount[0].rssi);
+ assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[0].count);
+ assertEquals(MAX_DELTA_LEVEL, mDeserializedWifiMetrics.rssiPollDeltaCount[1].rssi);
+ assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[1].count);
+ }
+
+ /**
+ * Tests the exact exclusive boundaries of RSSI delta logging.
+ * This test ensures that too much data is not generated.
+ */
+ @Test
+ public void testRssiDeltaOutOfBounds() throws Exception {
+ generateRssiDelta(MIN_RSSI_LEVEL, MAX_DELTA_LEVEL + 1,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
+ generateRssiDelta(MAX_RSSI_LEVEL, MIN_DELTA_LEVEL - 1,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
+ dumpProtoAndDeserialize();
+ assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ }
+
+ /**
+ * This test ensures no rssi Delta is logged after an unsuccessful ConnectionEvent
+ */
+ @Test
+ public void testUnsuccesfulConnectionEventRssiDeltaIsNotLogged() throws Exception {
+ generateRssiDelta(MIN_RSSI_LEVEL, ARBITRARY_DELTA_LEVEL,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS,
+ false, // successfulConnectionEvent
+ true, // completeConnectionEvent
+ true, // useValidScanResult
+ true // dontDeserializeBeforePoll
+ );
+
+ dumpProtoAndDeserialize();
+ assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ }
+
+ /**
+ * This test ensures rssi Deltas can be logged during a ConnectionEvent
+ */
+ @Test
+ public void testIncompleteConnectionEventRssiDeltaIsLogged() throws Exception {
+ generateRssiDelta(MIN_RSSI_LEVEL, ARBITRARY_DELTA_LEVEL,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS,
+ true, // successfulConnectionEvent
+ false, // completeConnectionEvent
+ true, // useValidScanResult
+ true // dontDeserializeBeforePoll
+ );
+ dumpProtoAndDeserialize();
+ assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ assertEquals(ARBITRARY_DELTA_LEVEL, mDeserializedWifiMetrics.rssiPollDeltaCount[0].rssi);
+ assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[0].count);
+ }
+
+ /**
+ * This test ensures that no delta is logged for a null ScanResult Candidate
+ */
+ @Test
+ public void testRssiDeltaNotLoggedForNullCandidateScanResult() throws Exception {
+ generateRssiDelta(MIN_RSSI_LEVEL, ARBITRARY_DELTA_LEVEL,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS,
+ true, // successfulConnectionEvent
+ true, // completeConnectionEvent
+ false, // useValidScanResult
+ true // dontDeserializeBeforePoll
+ );
+ dumpProtoAndDeserialize();
+ assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ }
+
+ /**
+ * This test ensures that Rssi Deltas are not logged over a 'clear()' call (Metrics Serialized)
+ */
+ @Test
+ public void testMetricsSerializedDuringRssiDeltaEventLogsNothing() throws Exception {
+ generateRssiDelta(MIN_RSSI_LEVEL, ARBITRARY_DELTA_LEVEL,
+ WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS,
+ true, // successfulConnectionEvent
+ true, // completeConnectionEvent
+ true, // useValidScanResult
+ false // dontDeserializeBeforePoll
+ );
+ dumpProtoAndDeserialize();
+ assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
+ }
+
+ private static final int DEAUTH_REASON = 7;
+ private static final int ASSOC_STATUS = 11;
+ private static final int ASSOC_TIMEOUT = 1;
+ private static final int LOCAL_GEN = 1;
+ private static final int AUTH_FAILURE_REASON = WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD;
+ private static final int NUM_TEST_STA_EVENTS = 14;
+ private static final String sSSID = "\"SomeTestSsid\"";
+ private static final WifiSsid sWifiSsid = WifiSsid.createFromAsciiEncoded(sSSID);
+ private static final String sBSSID = "01:02:03:04:05:06";
+
+ private final StateChangeResult mStateDisconnected =
+ new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED);
+ private final StateChangeResult mStateCompleted =
+ new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED);
+ // Test bitmasks of supplicant state changes
+ private final int mSupBm1 = WifiMetrics.supplicantStateToBit(mStateDisconnected.state);
+ private final int mSupBm2 = WifiMetrics.supplicantStateToBit(mStateDisconnected.state)
+ | WifiMetrics.supplicantStateToBit(mStateCompleted.state);
+ // An invalid but interesting wifiConfiguration that exercises the StaEvent.ConfigInfo encoding
+ private final WifiConfiguration mTestWifiConfig = createComplexWifiConfig();
+ // <msg.what> <msg.arg1> <msg.arg2>
+ private int[][] mTestStaMessageInts = {
+ {WifiMonitor.ASSOCIATION_REJECTION_EVENT, ASSOC_TIMEOUT, ASSOC_STATUS},
+ {WifiMonitor.AUTHENTICATION_FAILURE_EVENT, 0, AUTH_FAILURE_REASON},
+ {WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0},
+ {WifiMonitor.NETWORK_DISCONNECTION_EVENT, LOCAL_GEN, DEAUTH_REASON},
+ {WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0},
+ {WifiStateMachine.CMD_ASSOCIATED_BSSID, 0, 0},
+ {WifiStateMachine.CMD_TARGET_BSSID, 0, 0},
+ {WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0},
+ {WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0}
+ };
+ private Object[] mTestStaMessageObjs = {
+ null,
+ null,
+ null,
+ null,
+ mStateDisconnected,
+ null,
+ null,
+ mStateDisconnected,
+ mStateCompleted
+ };
+ // Values used to generate the StaEvent log calls from WifiStateMachine
+ // <StaEvent.Type>, <StaEvent.FrameworkDisconnectReason>, <1|0>(testWifiConfiguration, null)
+ private int[][] mTestStaLogInts = {
+ {StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL, 0, 0},
+ {StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST, 0, 0},
+ {StaEvent.TYPE_CMD_IP_REACHABILITY_LOST, 0, 0},
+ {StaEvent.TYPE_CMD_START_CONNECT, 0, 1},
+ {StaEvent.TYPE_CMD_START_ROAM, 0, 1},
+ {StaEvent.TYPE_CONNECT_NETWORK, 0, 1},
+ {StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK, 0, 0},
+ {StaEvent.TYPE_FRAMEWORK_DISCONNECT, StaEvent.DISCONNECT_API, 0}
+ };
+ // Values used to generate the StaEvent log calls from WifiMonitor
+ // <type>, <reason>, <status>, <local_gen>,
+ // <auth_fail_reason>, <assoc_timed_out> <supplicantStateChangeBitmask> <1|0>(has ConfigInfo)
+ private int[][] mExpectedValues = {
+ {StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT, -1, ASSOC_STATUS, 0,
+ /**/ 0, ASSOC_TIMEOUT, 0, 0}, /**/
+ {StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT, -1, -1, 0,
+ /**/StaEvent.AUTH_FAILURE_WRONG_PSWD, 0, 0, 0}, /**/
+ {StaEvent.TYPE_NETWORK_CONNECTION_EVENT, -1, -1, 0,
+ /**/ 0, 0, 0, 0}, /**/
+ {StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT, DEAUTH_REASON, -1, LOCAL_GEN,
+ /**/ 0, 0, 0, 0}, /**/
+ {StaEvent.TYPE_CMD_ASSOCIATED_BSSID, -1, -1, 0,
+ /**/ 0, 0, mSupBm1, 0}, /**/
+ {StaEvent.TYPE_CMD_TARGET_BSSID, -1, -1, 0,
+ /**/ 0, 0, 0, 0}, /**/
+ {StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL, -1, -1, 0,
+ /**/ 0, 0, mSupBm2, 0}, /**/
+ {StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST, -1, -1, 0,
+ /**/ 0, 0, 0, 0}, /**/
+ {StaEvent.TYPE_CMD_IP_REACHABILITY_LOST, -1, -1, 0,
+ /**/ 0, 0, 0, 0}, /**/
+ {StaEvent.TYPE_CMD_START_CONNECT, -1, -1, 0,
+ /**/ 0, 0, 0, 1}, /**/
+ {StaEvent.TYPE_CMD_START_ROAM, -1, -1, 0,
+ /**/ 0, 0, 0, 1}, /**/
+ {StaEvent.TYPE_CONNECT_NETWORK, -1, -1, 0,
+ /**/ 0, 0, 0, 1}, /**/
+ {StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK, -1, -1, 0,
+ /**/ 0, 0, 0, 0}, /**/
+ {StaEvent.TYPE_FRAMEWORK_DISCONNECT, -1, -1, 0,
+ /**/ 0, 0, 0, 0} /**/
+ };
+
+ /**
+ * Generates events from all the rows in mTestStaMessageInts, and then mTestStaLogInts
+ */
+ private void generateStaEvents(WifiMetrics wifiMetrics) {
+ Handler handler = wifiMetrics.getHandler();
+ for (int i = 0; i < mTestStaMessageInts.length; i++) {
+ int[] mia = mTestStaMessageInts[i];
+ handler.sendMessage(
+ handler.obtainMessage(mia[0], mia[1], mia[2], mTestStaMessageObjs[i]));
+ }
+ mTestLooper.dispatchAll();
+ for (int i = 0; i < mTestStaLogInts.length; i++) {
+ int[] lia = mTestStaLogInts[i];
+ wifiMetrics.logStaEvent(lia[0], lia[1], lia[2] == 1 ? mTestWifiConfig : null);
+ }
+ }
+ private void verifyDeserializedStaEvents(WifiMetricsProto.WifiLog wifiLog) {
+ assertEquals(NUM_TEST_STA_EVENTS, wifiLog.staEventList.length);
+ int j = 0; // De-serialized event index
+ for (int i = 0; i < mTestStaMessageInts.length; i++) {
+ StaEvent event = wifiLog.staEventList[j];
+ int[] mia = mTestStaMessageInts[i];
+ int[] evs = mExpectedValues[j];
+ if (mia[0] != WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT) {
+ assertEquals(evs[0], event.type);
+ assertEquals(evs[1], event.reason);
+ assertEquals(evs[2], event.status);
+ assertEquals(evs[3] == 1 ? true : false, event.localGen);
+ assertEquals(evs[4], event.authFailureReason);
+ assertEquals(evs[5] == 1 ? true : false, event.associationTimedOut);
+ assertEquals(evs[6], event.supplicantStateChangesBitmask);
+ assertConfigInfoEqualsWifiConfig(
+ evs[7] == 1 ? mTestWifiConfig : null, event.configInfo);
+ j++;
+ }
+ }
+ }
+
+ /**
+ * Generate StaEvents of each type, ensure all the different values are logged correctly,
+ * and that they survive serialization & de-serialization
+ */
+ @Test
+ public void testStaEventsLogSerializeDeserialize() throws Exception {
+ generateStaEvents(mWifiMetrics);
+ dumpProtoAndDeserialize();
+ verifyDeserializedStaEvents(mDeserializedWifiMetrics);
+ }
+
+ /**
+ * Ensure the number of StaEvents does not exceed MAX_STA_EVENTS by generating lots of events
+ * and checking how many are deserialized
+ */
+ @Test
+ public void testStaEventBounding() throws Exception {
+ for (int i = 0; i < (WifiMetrics.MAX_STA_EVENTS + 10); i++) {
+ mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT);
+ }
+ dumpProtoAndDeserialize();
+ assertEquals(WifiMetrics.MAX_STA_EVENTS, mDeserializedWifiMetrics.staEventList.length);
+ }
+
+ /**
+ * Ensure WifiMetrics doesn't cause a null pointer exception when called with null args
+ */
+ @Test
+ public void testDumpNullArg() {
+ mWifiMetrics.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null);
+ }
+
+ /**
+ * Generate an RSSI delta event by creating a connection event and an RSSI poll within
+ * 'interArrivalTime' milliseconds of each other.
+ * Event will not be logged if interArrivalTime > mWifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS
+ * successfulConnectionEvent, completeConnectionEvent, useValidScanResult and
+ * dontDeserializeBeforePoll
+ * each create an anomalous condition when set to false.
+ */
+ private void generateRssiDelta(int scanRssi, int rssiDelta,
+ long interArrivalTime, boolean successfulConnectionEvent,
+ boolean completeConnectionEvent, boolean useValidScanResult,
+ boolean dontDeserializeBeforePoll) throws Exception {
+ when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 0);
+ ScanResult scanResult = null;
+ if (useValidScanResult) {
+ scanResult = mock(ScanResult.class);
+ scanResult.level = scanRssi;
+ }
+ WifiConfiguration config = mock(WifiConfiguration.class);
+ WifiConfiguration.NetworkSelectionStatus networkSelectionStat =
+ mock(WifiConfiguration.NetworkSelectionStatus.class);
+ when(networkSelectionStat.getCandidate()).thenReturn(scanResult);
+ when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStat);
+ mWifiMetrics.startConnectionEvent(config, "TestNetwork",
+ WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
+ if (completeConnectionEvent) {
+ if (successfulConnectionEvent) {
+ mWifiMetrics.endConnectionEvent(
+ WifiMetrics.ConnectionEvent.FAILURE_NONE,
+ WifiMetricsProto.ConnectionEvent.HLF_NONE);
+ } else {
+ mWifiMetrics.endConnectionEvent(
+ WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
+ WifiMetricsProto.ConnectionEvent.HLF_NONE);
+ }
+ }
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(interArrivalTime);
+ if (!dontDeserializeBeforePoll) {
+ dumpProtoAndDeserialize();
+ }
+ mWifiMetrics.incrementRssiPollRssiCount(scanRssi + rssiDelta);
+ }
+ /**
+ * Generate an RSSI delta event, with all extra conditions set to true.
+ */
+ private void generateRssiDelta(int scanRssi, int rssiDelta,
+ long interArrivalTime) throws Exception {
+ generateRssiDelta(scanRssi, rssiDelta, interArrivalTime, true, true, true, true);
+ }
+
private void assertStringContains(
String actualString, String expectedSubstring) {
assertTrue("Expected text not found in: " + actualString,
@@ -673,6 +1103,56 @@
writer.flush();
return stream.toString();
}
+
+ private static final int TEST_ALLOWED_KEY_MANAGEMENT = 83;
+ private static final int TEST_ALLOWED_PROTOCOLS = 22;
+ private static final int TEST_ALLOWED_AUTH_ALGORITHMS = 11;
+ private static final int TEST_ALLOWED_PAIRWISE_CIPHERS = 67;
+ private static final int TEST_ALLOWED_GROUP_CIPHERS = 231;
+ private static final int TEST_CANDIDATE_LEVEL = -80;
+ private static final int TEST_CANDIDATE_FREQ = 2345;
+
+ private WifiConfiguration createComplexWifiConfig() {
+ WifiConfiguration config = new WifiConfiguration();
+ config.allowedKeyManagement = intToBitSet(TEST_ALLOWED_KEY_MANAGEMENT);
+ config.allowedProtocols = intToBitSet(TEST_ALLOWED_PROTOCOLS);
+ config.allowedAuthAlgorithms = intToBitSet(TEST_ALLOWED_AUTH_ALGORITHMS);
+ config.allowedPairwiseCiphers = intToBitSet(TEST_ALLOWED_PAIRWISE_CIPHERS);
+ config.allowedGroupCiphers = intToBitSet(TEST_ALLOWED_GROUP_CIPHERS);
+ config.hiddenSSID = true;
+ config.ephemeral = true;
+ config.getNetworkSelectionStatus().setHasEverConnected(true);
+ ScanResult candidate = new ScanResult();
+ candidate.level = TEST_CANDIDATE_LEVEL;
+ candidate.frequency = TEST_CANDIDATE_FREQ;
+ config.getNetworkSelectionStatus().setCandidate(candidate);
+ return config;
+ }
+
+ private void assertConfigInfoEqualsWifiConfig(WifiConfiguration config,
+ StaEvent.ConfigInfo info) {
+ if (config == null && info == null) return;
+ assertEquals(config.allowedKeyManagement, intToBitSet(info.allowedKeyManagement));
+ assertEquals(config.allowedProtocols, intToBitSet(info.allowedProtocols));
+ assertEquals(config.allowedAuthAlgorithms, intToBitSet(info.allowedAuthAlgorithms));
+ assertEquals(config.allowedPairwiseCiphers, intToBitSet(info.allowedPairwiseCiphers));
+ assertEquals(config.allowedGroupCiphers, intToBitSet(info.allowedGroupCiphers));
+ assertEquals(config.hiddenSSID, info.hiddenSsid);
+ assertEquals(config.ephemeral, info.isEphemeral);
+ assertEquals(config.getNetworkSelectionStatus().getHasEverConnected(),
+ info.hasEverConnected);
+ assertEquals(config.getNetworkSelectionStatus().getCandidate().level, info.scanRssi);
+ assertEquals(config.getNetworkSelectionStatus().getCandidate().frequency, info.scanFreq);
+ }
+
+ /**
+ * Sets the values of bitSet to match an int mask
+ */
+ private static BitSet intToBitSet(int mask) {
+ BitSet bitSet = new BitSet();
+ for (int bitIndex = 0; mask > 0; mask >>>= 1, bitIndex++) {
+ if ((mask & 1) != 0) bitSet.set(bitIndex);
+ }
+ return bitSet;
+ }
}
-
-
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java
new file mode 100644
index 0000000..64d7c87
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiMonitorTest.java
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback.WpsConfigError;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback.WpsErrorIndication;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Handler;
+import android.os.Message;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.hotspot2.AnqpEvent;
+import com.android.server.wifi.hotspot2.IconEvent;
+import com.android.server.wifi.util.TelephonyUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiMonitor}.
+ */
+@SmallTest
+public class WifiMonitorTest {
+ private static final String WLAN_IFACE_NAME = "wlan0";
+ private static final String SECOND_WLAN_IFACE_NAME = "wlan1";
+ private static final String[] GSM_AUTH_DATA = { "45adbc", "fead45", "0x3452"};
+ private static final String[] UMTS_AUTH_DATA = { "fead45", "0x3452"};
+ private static final String BSSID = "fe:45:23:12:12:0a";
+ private static final int NETWORK_ID = 5;
+ private static final String SSID = "\"test124\"";
+ private WifiMonitor mWifiMonitor;
+ private TestLooper mLooper;
+ private Handler mHandlerSpy;
+ private Handler mSecondHandlerSpy;
+
+ @Before
+ public void setUp() throws Exception {
+ mWifiMonitor = new WifiMonitor(mock(WifiInjector.class));
+ mLooper = new TestLooper();
+ mHandlerSpy = spy(new Handler(mLooper.getLooper()));
+ mSecondHandlerSpy = spy(new Handler(mLooper.getLooper()));
+ mWifiMonitor.setMonitoring(WLAN_IFACE_NAME, true);
+ }
+
+ /**
+ * Broadcast WPS failure event test.
+ */
+ @Test
+ public void testBroadcastWpsEventFailDueToErrorTkipOnlyProhibhited() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.WPS_FAIL_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastWpsFailEvent(
+ WLAN_IFACE_NAME, WpsConfigError.NO_ERROR,
+ WpsErrorIndication.SECURITY_TKIP_ONLY_PROHIBITED);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.WPS_FAIL_EVENT, messageCaptor.getValue().what);
+ assertEquals(WifiManager.WPS_TKIP_ONLY_PROHIBITED, messageCaptor.getValue().arg1);
+ }
+
+ /**
+ * Broadcast WPS failure event test.
+ */
+ @Test
+ public void testBroadcastWpsEventFailDueToErrorWepProhibhited() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.WPS_FAIL_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastWpsFailEvent(
+ WLAN_IFACE_NAME, WpsConfigError.NO_ERROR,
+ WpsErrorIndication.SECURITY_WEP_PROHIBITED);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.WPS_FAIL_EVENT, messageCaptor.getValue().what);
+ assertEquals(WifiManager.WPS_WEP_PROHIBITED, messageCaptor.getValue().arg1);
+ }
+
+ /**
+ * Broadcast WPS failure event test.
+ */
+ @Test
+ public void testBroadcastWpsEventFailDueToConfigAuthError() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.WPS_FAIL_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastWpsFailEvent(
+ WLAN_IFACE_NAME, WpsConfigError.DEV_PASSWORD_AUTH_FAILURE,
+ WpsErrorIndication.NO_ERROR);
+
+ mLooper.dispatchAll();
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.WPS_FAIL_EVENT, messageCaptor.getValue().what);
+ assertEquals(WifiManager.WPS_AUTH_FAILURE, messageCaptor.getValue().arg1);
+ }
+
+ /**
+ * Broadcast WPS failure event test.
+ */
+ @Test
+ public void testBroadcastWpsEventFailDueToConfigPbcOverlapError() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.WPS_FAIL_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastWpsFailEvent(
+ WLAN_IFACE_NAME, WpsConfigError.MULTIPLE_PBC_DETECTED,
+ WpsErrorIndication.NO_ERROR);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.WPS_FAIL_EVENT, messageCaptor.getValue().what);
+ assertEquals(WifiManager.WPS_OVERLAP_ERROR, messageCaptor.getValue().arg1);
+ }
+
+ /**
+ * Broadcast WPS failure event test.
+ */
+ @Test
+ public void testBroadcastWpsEventFailDueToConfigError() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.WPS_FAIL_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastWpsFailEvent(
+ WLAN_IFACE_NAME, WpsConfigError.MSG_TIMEOUT,
+ WpsErrorIndication.NO_ERROR);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.WPS_FAIL_EVENT, messageCaptor.getValue().what);
+ assertEquals(WifiManager.ERROR, messageCaptor.getValue().arg1);
+ assertEquals(WpsConfigError.MSG_TIMEOUT, messageCaptor.getValue().arg2);
+ }
+
+ /**
+ * Broadcast WPS success event test.
+ */
+ @Test
+ public void testBroadcastWpsEventSuccess() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.WPS_SUCCESS_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastWpsSuccessEvent(WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.WPS_SUCCESS_EVENT, messageCaptor.getValue().what);
+ }
+
+ /**
+ * Broadcast WPS overlap event test.
+ */
+ @Test
+ public void testBroadcastWpsEventOverlap() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.WPS_OVERLAP_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastWpsOverlapEvent(WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.WPS_OVERLAP_EVENT, messageCaptor.getValue().what);
+ }
+
+ /**
+ * Broadcast WPS timeout event test.
+ */
+ @Test
+ public void testBroadcastWpsEventTimeout() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.WPS_TIMEOUT_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastWpsTimeoutEvent(WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.WPS_TIMEOUT_EVENT, messageCaptor.getValue().what);
+ }
+
+ /**
+ * Broadcast ANQP done event test.
+ */
+ @Test
+ public void testBroadcastAnqpDoneEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.ANQP_DONE_EVENT, mHandlerSpy);
+ long bssid = 5;
+ mWifiMonitor.broadcastAnqpDoneEvent(WLAN_IFACE_NAME, new AnqpEvent(bssid, null));
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.ANQP_DONE_EVENT, messageCaptor.getValue().what);
+ assertEquals(bssid, ((AnqpEvent) messageCaptor.getValue().obj).getBssid());
+ assertNull(((AnqpEvent) messageCaptor.getValue().obj).getElements());
+ }
+
+ /**
+ * Broadcast Icon event test.
+ */
+ @Test
+ public void testBroadcastIconDoneEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.RX_HS20_ANQP_ICON_EVENT, mHandlerSpy);
+ long bssid = 5;
+ String fileName = "test";
+ int fileSize = 0;
+ mWifiMonitor.broadcastIconDoneEvent(
+ WLAN_IFACE_NAME, new IconEvent(bssid, fileName, fileSize, null));
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.RX_HS20_ANQP_ICON_EVENT, messageCaptor.getValue().what);
+ assertEquals(bssid, ((IconEvent) messageCaptor.getValue().obj).getBSSID());
+ assertEquals(fileName, ((IconEvent) messageCaptor.getValue().obj).getFileName());
+ assertEquals(fileSize, ((IconEvent) messageCaptor.getValue().obj).getSize());
+ assertNull(((IconEvent) messageCaptor.getValue().obj).getData());
+ }
+
+ /**
+ * Broadcast network Gsm auth request test.
+ */
+ @Test
+ public void testBroadcastNetworkGsmAuthRequestEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SUP_REQUEST_SIM_AUTH, mHandlerSpy);
+ int networkId = NETWORK_ID;
+ String ssid = SSID;
+ String[] data = GSM_AUTH_DATA;
+ mWifiMonitor.broadcastNetworkGsmAuthRequestEvent(WLAN_IFACE_NAME, networkId, ssid, data);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SUP_REQUEST_SIM_AUTH, messageCaptor.getValue().what);
+ TelephonyUtil.SimAuthRequestData authData =
+ (TelephonyUtil.SimAuthRequestData) messageCaptor.getValue().obj;
+ assertEquals(networkId, authData.networkId);
+ assertEquals(ssid, authData.ssid);
+ assertEquals(WifiEnterpriseConfig.Eap.SIM, authData.protocol);
+ assertArrayEquals(data, authData.data);
+ }
+
+ /**
+ * Broadcast network Umts auth request test.
+ */
+ @Test
+ public void testBroadcastNetworkUmtsAuthRequestEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SUP_REQUEST_SIM_AUTH, mHandlerSpy);
+ int networkId = NETWORK_ID;
+ String ssid = SSID;
+ String[] data = UMTS_AUTH_DATA;
+ mWifiMonitor.broadcastNetworkUmtsAuthRequestEvent(WLAN_IFACE_NAME, networkId, ssid, data);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SUP_REQUEST_SIM_AUTH, messageCaptor.getValue().what);
+ TelephonyUtil.SimAuthRequestData authData =
+ (TelephonyUtil.SimAuthRequestData) messageCaptor.getValue().obj;
+ assertEquals(networkId, authData.networkId);
+ assertEquals(ssid, authData.ssid);
+ assertEquals(WifiEnterpriseConfig.Eap.AKA, authData.protocol);
+ assertArrayEquals(data, authData.data);
+ }
+
+ /**
+ * Broadcast pno scan results event test.
+ */
+ @Test
+ public void testBroadcastPnoScanResultsEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.PNO_SCAN_RESULTS_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastPnoScanResultEvent(WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.PNO_SCAN_RESULTS_EVENT, messageCaptor.getValue().what);
+ }
+
+ /**
+ * Broadcast Scan results event test.
+ */
+ @Test
+ public void testBroadcastScanResultsEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SCAN_RESULTS_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastScanResultEvent(WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SCAN_RESULTS_EVENT, messageCaptor.getValue().what);
+ }
+
+ /**
+ * Broadcast Scan failed event test.
+ */
+ @Test
+ public void testBroadcastScanFailedEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SCAN_FAILED_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastScanFailedEvent(WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+
+ assertEquals(WifiMonitor.SCAN_FAILED_EVENT, messageCaptor.getValue().what);
+ }
+
+ /**
+ * Broadcast authentication failure test.
+ */
+ @Test
+ public void testBroadcastAuthenticationFailureEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.AUTHENTICATION_FAILURE_EVENT, mHandlerSpy);
+ int reason = WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD;
+ mWifiMonitor.broadcastAuthenticationFailureEvent(WLAN_IFACE_NAME, reason);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.AUTHENTICATION_FAILURE_EVENT, messageCaptor.getValue().what);
+ assertEquals(reason, messageCaptor.getValue().arg2);
+
+ }
+
+
+ /**
+ * Broadcast association rejection test.
+ */
+ @Test
+ public void testBroadcastAssociationRejectionEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.ASSOCIATION_REJECTION_EVENT, mHandlerSpy);
+ int status = 5;
+ String bssid = BSSID;
+ mWifiMonitor.broadcastAssociationRejectionEvent(WLAN_IFACE_NAME, status, false, bssid);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.ASSOCIATION_REJECTION_EVENT, messageCaptor.getValue().what);
+ assertEquals(0, messageCaptor.getValue().arg1);
+ assertEquals(status, messageCaptor.getValue().arg2);
+ assertEquals(bssid, (String) messageCaptor.getValue().obj);
+ }
+
+ /**
+ * Broadcast associated bssid test.
+ */
+ @Test
+ public void testBroadcastAssociatedBssidEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiStateMachine.CMD_ASSOCIATED_BSSID, mHandlerSpy);
+ String bssid = BSSID;
+ mWifiMonitor.broadcastAssociatedBssidEvent(WLAN_IFACE_NAME, bssid);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiStateMachine.CMD_ASSOCIATED_BSSID, messageCaptor.getValue().what);
+ assertEquals(bssid, (String) messageCaptor.getValue().obj);
+ }
+
+ /**
+ * Broadcast network connection test.
+ */
+ @Test
+ public void testBroadcastNetworkConnectionEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.NETWORK_CONNECTION_EVENT, mHandlerSpy);
+ int networkId = NETWORK_ID;
+ String bssid = BSSID;
+ mWifiMonitor.broadcastNetworkConnectionEvent(WLAN_IFACE_NAME, networkId, bssid);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.NETWORK_CONNECTION_EVENT, messageCaptor.getValue().what);
+ assertEquals(networkId, messageCaptor.getValue().arg1);
+ assertEquals(bssid, (String) messageCaptor.getValue().obj);
+ }
+
+ /**
+ * Broadcast network disconnection test.
+ */
+ @Test
+ public void testBroadcastNetworkDisconnectionEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.NETWORK_DISCONNECTION_EVENT, mHandlerSpy);
+ int local = 1;
+ int reason = 5;
+ String bssid = BSSID;
+ mWifiMonitor.broadcastNetworkDisconnectionEvent(WLAN_IFACE_NAME, local, reason, bssid);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.NETWORK_DISCONNECTION_EVENT, messageCaptor.getValue().what);
+ assertEquals(local, messageCaptor.getValue().arg1);
+ assertEquals(reason, messageCaptor.getValue().arg2);
+ assertEquals(bssid, (String) messageCaptor.getValue().obj);
+ }
+
+ /**
+ * Broadcast supplicant state change test.
+ */
+ @Test
+ public void testBroadcastSupplicantStateChangeEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, mHandlerSpy);
+ int networkId = NETWORK_ID;
+ WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(SSID);
+ String bssid = BSSID;
+ SupplicantState newState = SupplicantState.ASSOCIATED;
+ mWifiMonitor.broadcastSupplicantStateChangeEvent(
+ WLAN_IFACE_NAME, networkId, wifiSsid, bssid, newState);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, messageCaptor.getValue().what);
+ StateChangeResult result = (StateChangeResult) messageCaptor.getValue().obj;
+ assertEquals(networkId, result.networkId);
+ assertEquals(wifiSsid, result.wifiSsid);
+ assertEquals(bssid, result.BSSID);
+ assertEquals(newState, result.state);
+ }
+
+ /**
+ * Broadcast supplicant connection test.
+ */
+ @Test
+ public void testBroadcastSupplicantConnectionEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SUP_CONNECTION_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastSupplicantConnectionEvent(WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SUP_CONNECTION_EVENT, messageCaptor.getValue().what);
+ }
+ /**
+ * Broadcast supplicant disconnection test.
+ */
+ @Test
+ public void testBroadcastSupplicantDisconnectionEvent() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SUP_DISCONNECTION_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastSupplicantDisconnectionEvent(WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SUP_DISCONNECTION_EVENT, messageCaptor.getValue().what);
+ }
+ /**
+ * Broadcast message to two handlers test.
+ */
+ @Test
+ public void testBroadcastEventToTwoHandlers() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SUP_CONNECTION_EVENT, mHandlerSpy);
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SUP_CONNECTION_EVENT, mSecondHandlerSpy);
+ mWifiMonitor.broadcastSupplicantConnectionEvent(WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SUP_CONNECTION_EVENT, messageCaptor.getValue().what);
+ verify(mSecondHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SUP_CONNECTION_EVENT, messageCaptor.getValue().what);
+ }
+ /**
+ * Broadcast message when iface is null.
+ */
+ @Test
+ public void testBroadcastEventWhenIfaceIsNull() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SUP_DISCONNECTION_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastSupplicantDisconnectionEvent(null);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SUP_DISCONNECTION_EVENT, messageCaptor.getValue().what);
+ }
+ /**
+ * Broadcast message when iface handler is null.
+ */
+ @Test
+ public void testBroadcastEventWhenIfaceHandlerIsNull() {
+ mWifiMonitor.registerHandler(
+ WLAN_IFACE_NAME, WifiMonitor.SUP_DISCONNECTION_EVENT, mHandlerSpy);
+ mWifiMonitor.broadcastSupplicantDisconnectionEvent(SECOND_WLAN_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiMonitor.SUP_DISCONNECTION_EVENT, messageCaptor.getValue().what);
+ }
+}
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/WifiNativeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
index 7013fe3..2f13baf 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java
@@ -20,20 +20,25 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IClientInterface;
+import android.net.wifi.WifiConfiguration;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.HashSet;
+import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -41,16 +46,6 @@
*/
@SmallTest
public class WifiNativeTest {
- private static final int NETWORK_ID = 0;
- private static final String NETWORK_EXTRAS_VARIABLE = "test";
- private static final Map<String, String> NETWORK_EXTRAS_VALUES = new HashMap<>();
- static {
- NETWORK_EXTRAS_VALUES.put("key1", "value1");
- NETWORK_EXTRAS_VALUES.put("key2", "value2");
- }
- private static final String NETWORK_EXTRAS_SERIALIZED =
- "\"%7B%22key2%22%3A%22value2%22%2C%22key1%22%3A%22value1%22%7D\"";
-
private static final long FATE_REPORT_DRIVER_TIMESTAMP_USEC = 12345;
private static final byte[] FATE_REPORT_FRAME_BYTES = new byte[] {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 0, 1, 2, 3, 4, 5, 6, 7};
@@ -112,39 +107,55 @@
new FateMapping(WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER, "driver dropped (other)"),
new FateMapping((byte) 42, "42")
};
+ private static final WifiNative.SignalPollResult SIGNAL_POLL_RESULT =
+ new WifiNative.SignalPollResult() {{
+ currentRssi = -60;
+ txBitrate = 12;
+ associationFrequency = 5240;
+ }};
+ private static final WifiNative.TxPacketCounters PACKET_COUNTERS_RESULT =
+ new WifiNative.TxPacketCounters() {{
+ txSucceeded = 2000;
+ txFailed = 120;
+ }};
+ private static final Set<Integer> SCAN_FREQ_SET =
+ new HashSet<Integer>() {{
+ add(2410);
+ add(2450);
+ add(5050);
+ add(5200);
+ }};
+ private static final String TEST_QUOTED_SSID_1 = "\"testSsid1\"";
+ private static final String TEST_QUOTED_SSID_2 = "\"testSsid2\"";
+ private static final Set<String> SCAN_HIDDEN_NETWORK_SSID_SET =
+ new HashSet<String>() {{
+ add(TEST_QUOTED_SSID_1);
+ add(TEST_QUOTED_SSID_2);
+ }};
+
+ private static final WifiNative.PnoSettings TEST_PNO_SETTINGS =
+ new WifiNative.PnoSettings() {{
+ isConnected = false;
+ periodInMs = 6000;
+ networkList = new WifiNative.PnoNetwork[2];
+ networkList[0] = new WifiNative.PnoNetwork();
+ networkList[1] = new WifiNative.PnoNetwork();
+ networkList[0].ssid = TEST_QUOTED_SSID_1;
+ networkList[1].ssid = TEST_QUOTED_SSID_2;
+ }};
+
+ @Mock private WifiVendorHal mWifiVendorHal;
+ @Mock private WificondControl mWificondControl;
+ @Mock private SupplicantStaIfaceHal mStaIfaceHal;
private WifiNative mWifiNative;
@Before
public void setUp() throws Exception {
- final Constructor<WifiNative> wifiNativeConstructor =
- WifiNative.class.getDeclaredConstructor(String.class, Boolean.TYPE);
- wifiNativeConstructor.setAccessible(true);
- mWifiNative = spy(wifiNativeConstructor.newInstance("test", true));
- }
-
- /**
- * Verifies that setNetworkExtra() correctly writes a serialized and URL-encoded JSON object.
- */
- @Test
- public void testSetNetworkExtra() {
- when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true);
- assertTrue(mWifiNative.setNetworkExtra(NETWORK_ID, NETWORK_EXTRAS_VARIABLE,
- NETWORK_EXTRAS_VALUES));
- verify(mWifiNative).setNetworkVariable(NETWORK_ID, NETWORK_EXTRAS_VARIABLE,
- NETWORK_EXTRAS_SERIALIZED);
- }
-
- /**
- * Verifies that getNetworkExtra() correctly reads a serialized and URL-encoded JSON object.
- */
- @Test
- public void testGetNetworkExtra() {
- when(mWifiNative.getNetworkVariable(NETWORK_ID, NETWORK_EXTRAS_VARIABLE))
- .thenReturn(NETWORK_EXTRAS_SERIALIZED);
- final Map<String, String> actualValues =
- mWifiNative.getNetworkExtra(NETWORK_ID, NETWORK_EXTRAS_VARIABLE);
- assertEquals(NETWORK_EXTRAS_VALUES, actualValues);
+ MockitoAnnotations.initMocks(this);
+ when(mWifiVendorHal.isVendorHalSupported()).thenReturn(true);
+ when(mWifiVendorHal.startVendorHal(anyBoolean())).thenReturn(true);
+ mWifiNative = new WifiNative("test0", mWifiVendorHal, mStaIfaceHal, mWificondControl);
}
/**
@@ -181,6 +192,34 @@
assertArrayEquals(FATE_REPORT_FRAME_BYTES, fateReport.mFrameBytes);
}
+ /**
+ * Verifies the hashCode methods for HiddenNetwork and PnoNetwork classes
+ */
+ @Test
+ public void testHashCode() {
+ WifiNative.HiddenNetwork hiddenNet1 = new WifiNative.HiddenNetwork();
+ hiddenNet1.ssid = new String("sametext");
+
+ WifiNative.HiddenNetwork hiddenNet2 = new WifiNative.HiddenNetwork();
+ hiddenNet2.ssid = new String("sametext");
+
+ assertTrue(hiddenNet1.equals(hiddenNet2));
+ assertEquals(hiddenNet1.hashCode(), hiddenNet2.hashCode());
+
+ WifiNative.PnoNetwork pnoNet1 = new WifiNative.PnoNetwork();
+ pnoNet1.ssid = new String("sametext");
+ pnoNet1.flags = 2;
+ pnoNet1.auth_bit_field = 4;
+
+ WifiNative.PnoNetwork pnoNet2 = new WifiNative.PnoNetwork();
+ pnoNet2.ssid = new String("sametext");
+ pnoNet2.flags = 2;
+ pnoNet2.auth_bit_field = 4;
+
+ assertTrue(pnoNet1.equals(pnoNet2));
+ assertEquals(pnoNet1.hashCode(), pnoNet2.hashCode());
+ }
+
// Support classes for test{Tx,Rx}FateReportToString.
private static class FrameTypeMapping {
byte mTypeNumber;
@@ -439,4 +478,226 @@
}
// TODO(b/28005116): Add test for the success case of getDriverStateDump().
+
+ /**
+ * Verifies that setupDriverForClientMode() calls underlying WificondControl.
+ */
+ @Test
+ public void testSetupDriverForClientMode() {
+ IClientInterface clientInterface = mock(IClientInterface.class);
+ when(mWificondControl.setupDriverForClientMode()).thenReturn(clientInterface);
+
+ IClientInterface returnedClientInterface = mWifiNative.setupForClientMode();
+ assertEquals(clientInterface, returnedClientInterface);
+ verify(mWifiVendorHal).startVendorHal(eq(true));
+ verify(mWificondControl).setupDriverForClientMode();
+ }
+
+ /**
+ * Verifies that setupDriverForClientMode() does not call start vendor HAL when it is not
+ * supported and calls underlying WificondControl setup.
+ */
+ @Test
+ public void testSetupDriverForClientModeWithNoVendorHal() {
+ when(mWifiVendorHal.isVendorHalSupported()).thenReturn(false);
+ IClientInterface clientInterface = mock(IClientInterface.class);
+ when(mWificondControl.setupDriverForClientMode()).thenReturn(clientInterface);
+
+ IClientInterface returnedClientInterface = mWifiNative.setupForClientMode();
+ assertEquals(clientInterface, returnedClientInterface);
+ verify(mWifiVendorHal, never()).startVendorHal(anyBoolean());
+ verify(mWificondControl).setupDriverForClientMode();
+ }
+
+ /**
+ * Verifies that setupDriverForClientMode() returns null when underlying WificondControl
+ * call fails.
+ */
+ @Test
+ public void testSetupDriverForClientModeWificondError() {
+ when(mWificondControl.setupDriverForClientMode()).thenReturn(null);
+
+ IClientInterface returnedClientInterface = mWifiNative.setupForClientMode();
+ assertEquals(null, returnedClientInterface);
+ verify(mWifiVendorHal).startVendorHal(eq(true));
+ verify(mWificondControl).setupDriverForClientMode();
+ }
+
+ /**
+ * Verifies that setupDriverForClientMode() returns null when underlying Hal call fails.
+ */
+ @Test
+ public void testSetupDriverForClientModeHalError() {
+ when(mWifiVendorHal.startVendorHal(anyBoolean())).thenReturn(false);
+
+ IClientInterface returnedClientInterface = mWifiNative.setupForClientMode();
+ assertEquals(null, returnedClientInterface);
+ verify(mWifiVendorHal).startVendorHal(eq(true));
+ verify(mWificondControl, never()).setupDriverForClientMode();
+ }
+
+ /**
+ * Verifies that setupDriverForSoftApMode() calls underlying WificondControl.
+ */
+ @Test
+ public void testSetupDriverForSoftApMode() {
+ IApInterface apInterface = mock(IApInterface.class);
+ when(mWificondControl.setupDriverForSoftApMode()).thenReturn(apInterface);
+
+ IApInterface returnedApInterface = mWifiNative.setupForSoftApMode();
+ assertEquals(apInterface, returnedApInterface);
+ verify(mWifiVendorHal).startVendorHal(eq(false));
+ verify(mWificondControl).setupDriverForSoftApMode();
+ }
+
+ /**
+ * Verifies that setupDriverForClientMode() does not call start vendor HAL when it is not
+ * supported and calls underlying WificondControl setup.
+ */
+ @Test
+ public void testSetupDriverForSoftApModeWithNoVendorHal() {
+ when(mWifiVendorHal.isVendorHalSupported()).thenReturn(false);
+ IApInterface apInterface = mock(IApInterface.class);
+ when(mWificondControl.setupDriverForSoftApMode()).thenReturn(apInterface);
+
+ IApInterface returnedApInterface = mWifiNative.setupForSoftApMode();
+ assertEquals(apInterface, returnedApInterface);
+ verify(mWifiVendorHal, never()).startVendorHal(anyBoolean());
+ verify(mWificondControl).setupDriverForSoftApMode();
+ }
+
+ /**
+ * Verifies that setupDriverForSoftApMode() returns null when underlying WificondControl
+ * call fails.
+ */
+ @Test
+ public void testSetupDriverForSoftApModeWificondError() {
+ when(mWificondControl.setupDriverForSoftApMode()).thenReturn(null);
+ IApInterface returnedApInterface = mWifiNative.setupForSoftApMode();
+
+ assertEquals(null, returnedApInterface);
+ verify(mWifiVendorHal).startVendorHal(eq(false));
+ verify(mWificondControl).setupDriverForSoftApMode();
+ }
+
+ /**
+ * Verifies that setupDriverForSoftApMode() returns null when underlying Hal call fails.
+ */
+ @Test
+ public void testSetupDriverForSoftApModeHalError() {
+ when(mWifiVendorHal.startVendorHal(anyBoolean())).thenReturn(false);
+
+ IApInterface returnedApInterface = mWifiNative.setupForSoftApMode();
+ assertEquals(null, returnedApInterface);
+ verify(mWifiVendorHal).startVendorHal(eq(false));
+ verify(mWificondControl, never()).setupDriverForSoftApMode();
+ }
+
+ /**
+ * Verifies that enableSupplicant() calls underlying WificondControl.
+ */
+ @Test
+ public void testEnableSupplicant() {
+ when(mWificondControl.enableSupplicant()).thenReturn(true);
+
+ mWifiNative.enableSupplicant();
+ verify(mWificondControl).enableSupplicant();
+ }
+
+ /**
+ * Verifies that disableSupplicant() calls underlying WificondControl.
+ */
+ @Test
+ public void testDisableSupplicant() {
+ when(mWificondControl.disableSupplicant()).thenReturn(true);
+
+ mWifiNative.disableSupplicant();
+ verify(mWificondControl).disableSupplicant();
+ }
+
+ /**
+ * Verifies that tearDownInterfaces() calls underlying WificondControl.
+ */
+ @Test
+ public void testTearDown() {
+ when(mWificondControl.tearDownInterfaces()).thenReturn(true);
+
+ assertTrue(mWifiNative.tearDown());
+ verify(mWificondControl).tearDownInterfaces();
+ verify(mWifiVendorHal).stopVendorHal();
+ }
+
+ /**
+ * Verifies that signalPoll() calls underlying WificondControl.
+ */
+ @Test
+ public void testSignalPoll() throws Exception {
+ when(mWificondControl.signalPoll()).thenReturn(SIGNAL_POLL_RESULT);
+
+ assertEquals(SIGNAL_POLL_RESULT, mWifiNative.signalPoll());
+ verify(mWificondControl).signalPoll();
+ }
+
+ /**
+ * Verifies that getTxPacketCounters() calls underlying WificondControl.
+ */
+ @Test
+ public void testGetTxPacketCounters() throws Exception {
+ when(mWificondControl.getTxPacketCounters()).thenReturn(PACKET_COUNTERS_RESULT);
+
+ assertEquals(PACKET_COUNTERS_RESULT, mWifiNative.getTxPacketCounters());
+ verify(mWificondControl).getTxPacketCounters();
+ }
+
+ /**
+ * Verifies that scan() calls underlying WificondControl.
+ */
+ @Test
+ public void testScan() throws Exception {
+ mWifiNative.scan(SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET);
+ verify(mWificondControl).scan(SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET);
+ }
+
+ /**
+ * Verifies that startPnoscan() calls underlying WificondControl.
+ */
+ @Test
+ public void testStartPnoScan() throws Exception {
+ mWifiNative.startPnoScan(TEST_PNO_SETTINGS);
+ verify(mWificondControl).startPnoScan(TEST_PNO_SETTINGS);
+ }
+
+ /**
+ * Verifies that stopPnoscan() calls underlying WificondControl.
+ */
+ @Test
+ public void testStopPnoScan() throws Exception {
+ mWifiNative.stopPnoScan();
+ verify(mWificondControl).stopPnoScan();
+ }
+
+ /**
+ * Verifies that connectToNetwork() calls underlying WificondControl and SupplicantStaIfaceHal.
+ */
+ @Test
+ public void testConnectToNetwork() throws Exception {
+ WifiConfiguration config = mock(WifiConfiguration.class);
+ mWifiNative.connectToNetwork(config);
+ // connectToNetwork() should abort ongoing scan before connection.
+ verify(mWificondControl).abortScan();
+ verify(mStaIfaceHal).connectToNetwork(config);
+ }
+
+ /**
+ * Verifies that roamToNetwork() calls underlying WificondControl and SupplicantStaIfaceHal.
+ */
+ @Test
+ public void testRoamToNetwork() throws Exception {
+ WifiConfiguration config = mock(WifiConfiguration.class);
+ mWifiNative.roamToNetwork(config);
+ // roamToNetwork() should abort ongoing scan before connection.
+ verify(mWificondControl).abortScan();
+ verify(mStaIfaceHal).roamToNetwork(config);
+ }
+
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
new file mode 100644
index 0000000..8ad9e07
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java
@@ -0,0 +1,660 @@
+/*
+ * 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.WifiConfigurationTestUtil.SECURITY_NONE;
+import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiInfo;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.LocalLog;
+import android.util.Pair;
+
+import com.android.internal.R;
+import com.android.server.wifi.WifiNetworkSelectorTestUtil.ScanDetailsAndWifiConfigs;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiNetworkSelector}.
+ */
+@SmallTest
+public class WifiNetworkSelectorTest {
+
+ /** Sets up test. */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ setupContext();
+ setupResources();
+ setupWifiConfigManager();
+ setupWifiInfo();
+ mLocalLog = new LocalLog(512);
+
+ mWifiNetworkSelector = new WifiNetworkSelector(mContext, mWifiConfigManager, mClock,
+ mLocalLog);
+ mWifiNetworkSelector.registerNetworkEvaluator(mDummyEvaluator, 1);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime());
+
+ mThresholdMinimumRssi2G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz);
+ mThresholdMinimumRssi5G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz);
+ mThresholdQualifiedRssi2G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz);
+ mThresholdQualifiedRssi5G = mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz);
+ }
+
+ /** Cleans up test. */
+ @After
+ public void cleanup() {
+ validateMockitoUsage();
+ }
+
+ /**
+ * All this dummy network evaluator does is to pick the very first network
+ * in the scan results.
+ */
+ public class DummyNetworkEvaluator implements WifiNetworkSelector.NetworkEvaluator {
+ private static final String NAME = "DummyNetworkEvaluator";
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public void update(List<ScanDetail> scanDetails) {}
+
+ /**
+ * Always return the first network in the scan results for connection.
+ */
+ @Override
+ public WifiConfiguration evaluateNetworks(List<ScanDetail> scanDetails,
+ WifiConfiguration currentNetwork, String currentBssid, boolean connected,
+ boolean untrustedNetworkAllowed,
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks) {
+ ScanDetail scanDetail = scanDetails.get(0);
+ mWifiConfigManager.setNetworkCandidateScanResult(0, scanDetail.getScanResult(), 100);
+
+ return mWifiConfigManager.getSavedNetworkForScanDetailAndCache(scanDetail);
+ }
+ }
+
+ private WifiNetworkSelector mWifiNetworkSelector = null;
+ private DummyNetworkEvaluator mDummyEvaluator = new DummyNetworkEvaluator();
+ @Mock private WifiConfigManager mWifiConfigManager;
+ @Mock private Context mContext;
+ @Mock private Resources mResource;
+ @Mock private WifiInfo mWifiInfo;
+ @Mock private Clock mClock;
+ private LocalLog mLocalLog;
+ private int mThresholdMinimumRssi2G;
+ private int mThresholdMinimumRssi5G;
+ private int mThresholdQualifiedRssi2G;
+ private int mThresholdQualifiedRssi5G;
+
+ private void setupContext() {
+ when(mContext.getResources()).thenReturn(mResource);
+ }
+
+ private void setupResources() {
+ when(mResource.getBoolean(
+ R.bool.config_wifi_framework_enable_associated_network_selection)).thenReturn(true);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz))
+ .thenReturn(-70);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz))
+ .thenReturn(-73);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz))
+ .thenReturn(-82);
+ when(mResource.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz))
+ .thenReturn(-85);
+ }
+
+ private void setupWifiInfo() {
+ // simulate a disconnected state
+ when(mWifiInfo.is24GHz()).thenReturn(true);
+ when(mWifiInfo.is5GHz()).thenReturn(false);
+ when(mWifiInfo.getRssi()).thenReturn(-70);
+ when(mWifiInfo.getNetworkId()).thenReturn(WifiConfiguration.INVALID_NETWORK_ID);
+ when(mWifiInfo.getBSSID()).thenReturn(null);
+ }
+
+ private void setupWifiConfigManager() {
+ when(mWifiConfigManager.getLastSelectedNetwork())
+ .thenReturn(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+
+ /**
+ * No network selection if scan result is empty.
+ *
+ * WifiStateMachine is in disconnected state.
+ * scanDetails is empty.
+ *
+ * Expected behavior: no network recommended by Network Selector
+ */
+ @Test
+ public void emptyScanResults() {
+ String[] ssids = new String[0];
+ String[] bssids = new String[0];
+ int[] freqs = new int[0];
+ String[] caps = new String[0];
+ int[] levels = new int[0];
+ int[] securities = new int[0];
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+ assertEquals("Expect null configuration", null, candidate);
+ }
+
+
+ /**
+ * No network selection if the RSSI values in scan result are too low.
+ *
+ * WifiStateMachine is in disconnected state.
+ * scanDetails contains a 2.4GHz and a 5GHz network, but both with RSSI lower than
+ * the threshold
+ *
+ * Expected behavior: no network recommended by Network Selector
+ */
+ @Test
+ public void verifyMinimumRssiThreshold() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G - 1, mThresholdMinimumRssi5G - 1};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+ assertEquals("Expect null configuration", null, candidate);
+ }
+
+ /**
+ * No network selection if WiFi is connected and it is too short from last
+ * network selection.
+ *
+ * WifiStateMachine is in connected state.
+ * scanDetails contains two valid networks.
+ * Perform a network seletion right after the first one.
+ *
+ * Expected behavior: no network recommended by Network Selector
+ */
+ @Test
+ public void verifyMinimumTimeGapWhenConnected() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ // Make a network selection.
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS - 2000);
+
+ // Do another network selection with WSM in CONNECTED state.
+ candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, true, false, false);
+
+ assertEquals("Expect null configuration", null, candidate);
+ }
+
+ /**
+ * Perform network selection if WiFi is disconnected even if it is too short from last
+ * network selection.
+ *
+ * WifiStateMachine is in disconnected state.
+ * scanDetails contains two valid networks.
+ * Perform a network seletion right after the first one.
+ *
+ * Expected behavior: the first network is recommended by Network Selector
+ */
+ @Test
+ public void verifyNoMinimumTimeGapWhenDisconnected() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ // Make a network selection.
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+ HashSet<String> blacklist = new HashSet<String>();
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS - 2000);
+
+ // Do another network selection with WSM in DISCONNECTED state.
+ candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+
+ ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+ /**
+ * No network selection if the currently connected on is already sufficient.
+ *
+ * WifiStateMachine is connected to a qualified (5G, secure, good RSSI) network.
+ * scanDetails contains a valid network.
+ * Perform a network seletion after the first one.
+ *
+ * Expected behavior: no network recommended by Network Selector
+ */
+ @Test
+ public void noNetworkSelectionWhenCurrentOneIsSufficient() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi5G + 8};
+ int[] securities = {SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+
+ // connect to test1
+ mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
+ when(mWifiInfo.getNetworkId()).thenReturn(0);
+ when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
+ when(mWifiInfo.is24GHz()).thenReturn(false);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
+
+ levels[0] = mThresholdQualifiedRssi5G + 20;
+ scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ scanDetails = scanDetailsAndConfigs.getScanDetails();
+
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, true, false, false);
+ assertEquals("Expect null configuration", null, candidate);
+ }
+
+ /**
+ * New network selection is performed if the currently connected network
+ * band is 2G.
+ *
+ * WifiStateMachine is connected to a 2G network.
+ * scanDetails contains a valid networks.
+ * Perform a network seletion after the first one.
+ *
+ * Expected behavior: the first network is recommended by Network Selector
+ */
+ @Test
+ public void band2GNetworkIsNotSufficient() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {2470};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi2G + 8};
+ int[] securities = {SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ // connect to test1
+ mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
+ when(mWifiInfo.getNetworkId()).thenReturn(0);
+ when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
+ when(mWifiInfo.is24GHz()).thenReturn(true);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
+
+ // Do another network selection.
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, true, false, false);
+
+ ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+
+ /**
+ * New network selection is performed if the currently connected network
+ * is a open one.
+ *
+ * WifiStateMachine is connected to a open network.
+ * scanDetails contains a valid networks.
+ * Perform a network seletion after the first one.
+ *
+ * Expected behavior: the first network is recommended by Network Selector
+ */
+ @Test
+ public void openNetworkIsNotSufficient() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {5180};
+ String[] caps = {"[ESS]"};
+ int[] levels = {mThresholdQualifiedRssi5G + 8};
+ int[] securities = {SECURITY_NONE};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ // connect to test1
+ mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
+ when(mWifiInfo.getNetworkId()).thenReturn(0);
+ when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
+ when(mWifiInfo.is24GHz()).thenReturn(false);
+ when(mWifiInfo.is5GHz()).thenReturn(true);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
+
+ // Do another network selection.
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, true, false, false);
+
+ ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+ /**
+ * New network selection is performed if the currently connected network
+ * has low RSSI value.
+ *
+ * WifiStateMachine is connected to a low RSSI 5GHz network.
+ * scanDetails contains a valid networks.
+ * Perform a network seletion after the first one.
+ *
+ * Expected behavior: the first network is recommended by Network Selector
+ */
+ @Test
+ public void lowRssi5GNetworkIsNotSufficient() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi5G - 2};
+ int[] securities = {SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+ WifiConfiguration[] savedConfigs = scanDetailsAndConfigs.getWifiConfigs();
+
+ // connect to test1
+ mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo, false, true, false);
+ when(mWifiInfo.getNetworkId()).thenReturn(0);
+ when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
+ when(mWifiInfo.is24GHz()).thenReturn(false);
+ when(mWifiInfo.is5GHz()).thenReturn(true);
+ when(mWifiInfo.getRssi()).thenReturn(levels[0]);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
+
+ // Do another network selection.
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, true, false, false);
+
+ ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
+ WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate);
+ WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager,
+ chosenScanResult, candidate);
+ }
+
+ /**
+ * Blacklisted BSSID is filtered out for network selection.
+ *
+ * WifiStateMachine is disconnected.
+ * scanDetails contains a network which is blacklisted.
+ *
+ * Expected behavior: no network recommended by Network Selector
+ */
+ @Test
+ public void filterOutBlacklistedBssid() {
+ String[] ssids = {"\"test1\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3"};
+ int[] freqs = {5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdQualifiedRssi5G + 8};
+ int[] securities = {SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+ blacklist.add(bssids[0]);
+
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+ assertEquals("Expect null configuration", null, candidate);
+ }
+
+ /**
+ * Wifi network selector doesn't recommend any network if the currently connected one
+ * doesn't show up in the scan results.
+ *
+ * WifiStateMachine is under connected state and 2.4GHz test1 is connected.
+ * The second scan results contains only test2 which now has a stronger RSSI than test1.
+ * Test1 is not in the second scan results.
+ *
+ * Expected behavior: no network recommended by Network Selector
+ */
+ @Test
+ public void noSelectionWhenCurrentNetworkNotInScanResults() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 2457};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 20, mThresholdMinimumRssi2G + 1};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ // Make a network selection to connect to test1.
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+
+ when(mWifiInfo.getNetworkId()).thenReturn(0);
+ when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
+ when(mWifiInfo.is24GHz()).thenReturn(true);
+ when(mWifiInfo.is5GHz()).thenReturn(false);
+ when(mWifiInfo.getRssi()).thenReturn(levels[0]);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
+
+ // Prepare the second scan results which have no test1.
+ String[] ssidsNew = {"\"test2\""};
+ String[] bssidsNew = {"6c:f3:7f:ae:8c:f4"};
+ int[] freqsNew = {2457};
+ String[] capsNew = {"[WPA2-EAP-CCMP][ESS]"};
+ int[] levelsNew = {mThresholdMinimumRssi2G + 40};
+ scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails(ssidsNew, bssidsNew,
+ freqsNew, capsNew, levelsNew, mClock);
+ candidate = mWifiNetworkSelector.selectNetwork(scanDetails, blacklist, mWifiInfo,
+ true, false, false);
+
+ // The second network selection is skipped since current connected network is
+ // missing from the scan results.
+ assertEquals("Expect null configuration", null, candidate);
+ }
+
+ /**
+ * Ensures that settings the user connect choice updates the
+ * NetworkSelectionStatus#mConnectChoice for all other WifiConfigurations in range in the last
+ * round of network selection.
+ *
+ * Expected behavior: WifiConfiguration.NetworkSelectionStatus#mConnectChoice is set to
+ * test1's configkey for test2. test3's WifiConfiguration is unchanged.
+ */
+ @Test
+ public void setUserConnectChoice() {
+ String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"};
+ int[] freqs = {2437, 5180, 5181};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1,
+ mThresholdMinimumRssi5G + 1};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+
+ WifiConfiguration selectedWifiConfig = scanDetailsAndConfigs.getWifiConfigs()[0];
+ selectedWifiConfig.getNetworkSelectionStatus()
+ .setCandidate(scanDetailsAndConfigs.getScanDetails().get(0).getScanResult());
+ selectedWifiConfig.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED);
+ selectedWifiConfig.getNetworkSelectionStatus().setConnectChoice("bogusKey");
+
+ WifiConfiguration configInLastNetworkSelection = scanDetailsAndConfigs.getWifiConfigs()[1];
+ configInLastNetworkSelection.getNetworkSelectionStatus()
+ .setSeenInLastQualifiedNetworkSelection(true);
+
+ WifiConfiguration configNotInLastNetworkSelection =
+ scanDetailsAndConfigs.getWifiConfigs()[2];
+
+ assertTrue(mWifiNetworkSelector.setUserConnectChoice(selectedWifiConfig.networkId));
+
+ verify(mWifiConfigManager).updateNetworkSelectionStatus(selectedWifiConfig.networkId,
+ NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+ verify(mWifiConfigManager).clearNetworkConnectChoice(selectedWifiConfig.networkId);
+ verify(mWifiConfigManager).setNetworkConnectChoice(configInLastNetworkSelection.networkId,
+ selectedWifiConfig.configKey(), mClock.getWallClockMillis());
+ verify(mWifiConfigManager, never()).setNetworkConnectChoice(
+ configNotInLastNetworkSelection.networkId, selectedWifiConfig.configKey(),
+ mClock.getWallClockMillis());
+ }
+
+ /**
+ * If two qualified networks, test1 and test2, are in range when the user selects test2 over
+ * test1, WifiNetworkSelector will override the NetworkSelector's choice to connect to test1
+ * with test2.
+ *
+ * Expected behavior: test2 is the recommended network
+ */
+ @Test
+ public void userConnectChoiceOverridesNetworkEvaluators() {
+ String[] ssids = {"\"test1\"", "\"test2\""};
+ String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
+ int[] freqs = {2437, 5180};
+ String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
+ int[] levels = {mThresholdMinimumRssi2G + 1, mThresholdMinimumRssi5G + 1};
+ int[] securities = {SECURITY_PSK, SECURITY_PSK};
+
+ ScanDetailsAndWifiConfigs scanDetailsAndConfigs =
+ WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids,
+ freqs, caps, levels, securities, mWifiConfigManager, mClock);
+ List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails();
+ HashSet<String> blacklist = new HashSet<String>();
+
+ // DummyEvaluator always selects the first network in the list.
+ WifiConfiguration networkSelectorChoice = scanDetailsAndConfigs.getWifiConfigs()[0];
+ networkSelectorChoice.getNetworkSelectionStatus()
+ .setSeenInLastQualifiedNetworkSelection(true);
+
+ WifiConfiguration userChoice = scanDetailsAndConfigs.getWifiConfigs()[1];
+ userChoice.getNetworkSelectionStatus()
+ .setCandidate(scanDetailsAndConfigs.getScanDetails().get(1).getScanResult());
+
+ // With no user choice set, networkSelectorChoice should be chosen.
+ WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(networkSelectorChoice, candidate);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(SystemClock.elapsedRealtime()
+ + WifiNetworkSelector.MINIMUM_NETWORK_SELECTION_INTERVAL_MS + 2000);
+
+ assertTrue(mWifiNetworkSelector.setUserConnectChoice(userChoice.networkId));
+
+ // After user connect choice is set, userChoice should override networkSelectorChoice.
+ candidate = mWifiNetworkSelector.selectNetwork(scanDetails,
+ blacklist, mWifiInfo, false, true, false);
+
+ WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java
new file mode 100644
index 0000000..9c78c9b
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java
@@ -0,0 +1,403 @@
+/*
+ * 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.WifiConfigurationTestUtil.generateWifiConfig;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.net.NetworkKey;
+import android.net.RssiCurve;
+import android.net.ScoredNetwork;
+import android.net.WifiKey;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.WifiSsid;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+
+import com.android.server.wifi.util.ScanResultUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper for WifiNetworkSelector unit tests.
+ */
+@SmallTest
+public class WifiNetworkSelectorTestUtil {
+
+ /**
+ * A class that holds a list of scanDetail and their associated WifiConfiguration.
+ */
+ public static class ScanDetailsAndWifiConfigs {
+ List<ScanDetail> mScanDetails;
+ WifiConfiguration[] mWifiConfigs;
+
+ ScanDetailsAndWifiConfigs(List<ScanDetail> scanDetails, WifiConfiguration[] configs) {
+ mScanDetails = scanDetails;
+ mWifiConfigs = configs;
+ }
+
+ List<ScanDetail> getScanDetails() {
+ return mScanDetails;
+ }
+
+ WifiConfiguration[] getWifiConfigs() {
+ return mWifiConfigs;
+ }
+ }
+
+ /**
+ * Build a list of ScanDetail based on the caller supplied network SSID, BSSID,
+ * frequency, capability and RSSI level information. Create the corresponding
+ * WifiConfiguration for these networks and set up the mocked WifiConfigManager.
+ *
+ * @param ssids an array of SSIDs
+ * @param bssids an array of BSSIDs
+ * @param freqs an array of the network's frequency
+ * @param caps an array of the network's capability
+ * @param levels an array of the network's RSSI levels
+ * @param securities an array of the network's security setting
+ * @param wifiConfigManager the mocked WifiConfigManager
+ * @return the constructed ScanDetail list and WifiConfiguration array
+ */
+ public static ScanDetailsAndWifiConfigs setupScanDetailsAndConfigStore(String[] ssids,
+ String[] bssids, int[] freqs, String[] caps, int[] levels, int[] securities,
+ WifiConfigManager wifiConfigManager, Clock clock) {
+ List<ScanDetail> scanDetails = buildScanDetails(ssids, bssids, freqs, caps, levels, clock);
+ WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, securities);
+ prepareConfigStore(wifiConfigManager, savedConfigs);
+ scanResultLinkConfiguration(wifiConfigManager, savedConfigs, scanDetails);
+
+ return new ScanDetailsAndWifiConfigs(scanDetails, savedConfigs);
+ }
+
+ /**
+ * Verify whether the WifiConfiguration chosen by WifiNetworkSelector matches
+ * with the chosen scan result.
+ *
+ * @param chosenScanResult the chosen scan result
+ * @param chosenCandidate the chosen configuration
+ */
+ public static void verifySelectedScanResult(WifiConfigManager wifiConfigManager,
+ ScanResult chosenScanResult, WifiConfiguration chosenCandidate) {
+ verify(wifiConfigManager, atLeastOnce()).setNetworkCandidateScanResult(
+ eq(chosenCandidate.networkId), eq(chosenScanResult), anyInt());
+ }
+
+
+ /**
+ * Build a list of scanDetails based on the caller supplied network SSID, BSSID,
+ * frequency, capability and RSSI level information.
+ *
+ * @param ssids an array of SSIDs
+ * @param bssids an array of BSSIDs
+ * @param freqs an array of the network's frequency
+ * @param caps an array of the network's capability
+ * @param levels an array of the network's RSSI levels
+ * @return the constructed list of ScanDetail
+ */
+ public static List<ScanDetail> buildScanDetails(String[] ssids, String[] bssids, int[] freqs,
+ String[] caps, int[] levels, Clock clock) {
+ List<ScanDetail> scanDetailList = new ArrayList<ScanDetail>();
+
+ long timeStamp = clock.getElapsedSinceBootMillis();
+ for (int index = 0; index < ssids.length; index++) {
+ ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssids[index]),
+ bssids[index], caps[index], levels[index], freqs[index], timeStamp, 0);
+ scanDetailList.add(scanDetail);
+ }
+ return scanDetailList;
+ }
+
+
+ /**
+ * Generate an array of {@link android.net.wifi.WifiConfiguration} based on the caller
+ * supplied network SSID and security information.
+ *
+ * @param ssids an array of SSIDs
+ * @param securities an array of the network's security setting
+ * @return the constructed array of {@link android.net.wifi.WifiConfiguration}
+ */
+ public static WifiConfiguration[] generateWifiConfigurations(String[] ssids,
+ int[] securities) {
+ if (ssids == null || securities == null || ssids.length != securities.length
+ || ssids.length == 0) {
+ return null;
+ }
+
+ Map<String, Integer> netIdMap = new HashMap<>();
+ int netId = 0;
+
+ WifiConfiguration[] configs = new WifiConfiguration[ssids.length];
+ for (int index = 0; index < ssids.length; index++) {
+ String configKey = ssids[index] + Integer.toString(securities[index]);
+ Integer id;
+
+ id = netIdMap.get(configKey);
+ if (id == null) {
+ id = new Integer(netId);
+ netIdMap.put(configKey, id);
+ netId++;
+ }
+
+ configs[index] = generateWifiConfig(id.intValue(), 0, ssids[index], false, true, null,
+ null, securities[index]);
+ }
+
+ return configs;
+ }
+
+ /**
+ * Add the Configurations to WifiConfigManager (WifiConfigureStore can take them out according
+ * to the networkd ID) and setup the WifiConfigManager mocks for these networks.
+ * This simulates the WifiConfigManager class behaviour.
+ *
+ * @param wifiConfigManager the mocked WifiConfigManager
+ * @param configs input configuration need to be added to WifiConfigureStore
+ */
+ private static void prepareConfigStore(final WifiConfigManager wifiConfigManager,
+ final WifiConfiguration[] configs) {
+ when(wifiConfigManager.getConfiguredNetwork(anyInt()))
+ .then(new AnswerWithArguments() {
+ public WifiConfiguration answer(int netId) {
+ for (WifiConfiguration config : configs) {
+ if (netId == config.networkId) {
+ return new WifiConfiguration(config);
+ }
+ }
+ return null;
+ }
+ });
+ when(wifiConfigManager.getConfiguredNetwork(anyString()))
+ .then(new AnswerWithArguments() {
+ public WifiConfiguration answer(String configKey) {
+ for (WifiConfiguration config : configs) {
+ if (TextUtils.equals(config.configKey(), configKey)) {
+ return new WifiConfiguration(config);
+ }
+ }
+ return null;
+ }
+ });
+ when(wifiConfigManager.getSavedNetworks())
+ .then(new AnswerWithArguments() {
+ public List<WifiConfiguration> answer() {
+ List<WifiConfiguration> savedNetworks = new ArrayList<>();
+ for (int netId = 0; netId < configs.length; netId++) {
+ savedNetworks.add(new WifiConfiguration(configs[netId]));
+ }
+ return savedNetworks;
+ }
+ });
+ when(wifiConfigManager.clearNetworkCandidateScanResult(anyInt()))
+ .then(new AnswerWithArguments() {
+ public boolean answer(int netId) {
+ if (netId >= 0 && netId < configs.length) {
+ configs[netId].getNetworkSelectionStatus().setCandidate(null);
+ configs[netId].getNetworkSelectionStatus()
+ .setCandidateScore(Integer.MIN_VALUE);
+ configs[netId].getNetworkSelectionStatus()
+ .setSeenInLastQualifiedNetworkSelection(false);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+ when(wifiConfigManager.setNetworkCandidateScanResult(
+ anyInt(), any(ScanResult.class), anyInt()))
+ .then(new AnswerWithArguments() {
+ public boolean answer(int netId, ScanResult scanResult, int score) {
+ if (netId >= 0 && netId < configs.length) {
+ configs[netId].getNetworkSelectionStatus().setCandidate(scanResult);
+ configs[netId].getNetworkSelectionStatus().setCandidateScore(score);
+ configs[netId].getNetworkSelectionStatus()
+ .setSeenInLastQualifiedNetworkSelection(true);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+ when(wifiConfigManager.clearNetworkConnectChoice(anyInt()))
+ .then(new AnswerWithArguments() {
+ public boolean answer(int netId) {
+ if (netId >= 0 && netId < configs.length) {
+ configs[netId].getNetworkSelectionStatus().setConnectChoice(null);
+ configs[netId].getNetworkSelectionStatus()
+ .setConnectChoiceTimestamp(
+ NetworkSelectionStatus
+ .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+ when(wifiConfigManager.setNetworkConnectChoice(anyInt(), anyString(), anyLong()))
+ .then(new AnswerWithArguments() {
+ public boolean answer(int netId, String configKey, long timestamp) {
+ if (netId >= 0 && netId < configs.length) {
+ configs[netId].getNetworkSelectionStatus().setConnectChoice(configKey);
+ configs[netId].getNetworkSelectionStatus().setConnectChoiceTimestamp(
+ timestamp);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ });
+ }
+
+
+ /**
+ * Link scan results to the saved configurations.
+ *
+ * The shorter of the 2 input params will be used to loop over so the inputs don't
+ * need to be of equal length. If there are more scan details then configs the remaining scan
+ * details will be associated with a NULL config.
+ *
+ * @param wifiConfigManager the mocked WifiConfigManager
+ * @param configs saved configurations
+ * @param scanDetails come in scan results
+ */
+ private static void scanResultLinkConfiguration(WifiConfigManager wifiConfigManager,
+ WifiConfiguration[] configs, List<ScanDetail> scanDetails) {
+ if (configs == null || scanDetails == null) {
+ return;
+ }
+
+ if (scanDetails.size() <= configs.length) {
+ for (int i = 0; i < scanDetails.size(); i++) {
+ ScanDetail scanDetail = scanDetails.get(i);
+ when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail)))
+ .thenReturn(configs[i]);
+ }
+ } else {
+ for (int i = 0; i < configs.length; i++) {
+ ScanDetail scanDetail = scanDetails.get(i);
+ when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail)))
+ .thenReturn(configs[i]);
+ }
+
+ // associated the remaining scan details with a NULL config.
+ for (int i = configs.length; i < scanDetails.size(); i++) {
+ when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(
+ eq(scanDetails.get(i)))).thenReturn(null);
+ }
+ }
+ }
+
+
+ /**
+ * Configure the score cache for externally scored networks
+ *
+ * @param scoreCache Wifi network score cache to be configured
+ * @param scanDetails a list of ScanDetail
+ * @param scores scores of the networks
+ * @param meteredHints hints of if the networks are metered
+ */
+ public static void configureScoreCache(WifiNetworkScoreCache scoreCache,
+ List<ScanDetail> scanDetails, Integer[] scores, boolean[] meteredHints) {
+ List<ScoredNetwork> networks = new ArrayList<>();
+
+ for (int i = 0; i < scanDetails.size(); i++) {
+ ScanDetail scanDetail = scanDetails.get(i);
+ ScanResult scanResult = scanDetail.getScanResult();
+ WifiKey wifiKey = new WifiKey("\"" + scanResult.SSID + "\"", scanResult.BSSID);
+ NetworkKey ntwkKey = new NetworkKey(wifiKey);
+ RssiCurve rssiCurve;
+
+ if (scores != null) { // fixed score
+ byte rssiScore;
+ Integer score = scores[i];
+
+ if (scores[i] == null) {
+ rssiScore = WifiNetworkScoreCache.INVALID_NETWORK_SCORE;
+ } else {
+ rssiScore = scores[i].byteValue();
+ }
+ rssiCurve = new RssiCurve(-100, 100, new byte[] {rssiScore});
+ } else {
+ rssiCurve = new RssiCurve(-80, 20, new byte[] {-10, 0, 10, 20, 30, 40});
+ }
+ ScoredNetwork scoredNetwork = new ScoredNetwork(ntwkKey, rssiCurve, meteredHints[i]);
+
+ networks.add(scoredNetwork);
+ }
+
+ scoreCache.updateScores(networks);
+ }
+
+ /**
+ * Setup WifiConfigManager mock for ephemeral networks.
+ *
+ * @param wifiConfigManager WifiConfigManager mock
+ * @param networkId ID of the ephemeral network
+ * @param scanDetail scanDetail of the ephemeral network
+ * @param meteredHint flag to indidate if the network has meteredHint
+ */
+ public static WifiConfiguration setupEphemeralNetwork(WifiConfigManager wifiConfigManager,
+ int networkId, ScanDetail scanDetail, boolean meteredHint) {
+ // Return the correct networkID for ephemeral network addition.
+ when(wifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+ .thenReturn(new NetworkUpdateResult(networkId));
+ final WifiConfiguration config =
+ ScanResultUtil.createNetworkFromScanResult(scanDetail.getScanResult());
+ config.ephemeral = true;
+ config.networkId = networkId;
+ config.meteredHint = meteredHint;
+
+ when(wifiConfigManager.getSavedNetworkForScanDetailAndCache(eq(scanDetail)))
+ .thenReturn(new WifiConfiguration(config));
+ when(wifiConfigManager.getConfiguredNetwork(eq(networkId)))
+ .then(new AnswerWithArguments() {
+ public WifiConfiguration answer(int netId) {
+ return new WifiConfiguration(config);
+ }
+ });
+ when(wifiConfigManager.setNetworkCandidateScanResult(
+ eq(networkId), any(ScanResult.class), anyInt()))
+ .then(new AnswerWithArguments() {
+ public boolean answer(int netId, ScanResult scanResult, int score) {
+ config.getNetworkSelectionStatus().setCandidate(scanResult);
+ config.getNetworkSelectionStatus().setCandidateScore(score);
+ config.getNetworkSelectionStatus()
+ .setSeenInLastQualifiedNetworkSelection(true);
+ return true;
+ }
+ });
+ when(wifiConfigManager.updateNetworkSelectionStatus(eq(networkId),
+ eq(WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE)))
+ .then(new AnswerWithArguments() {
+ public boolean answer(int netId, int status) {
+ config.getNetworkSelectionStatus().setNetworkSelectionStatus(
+ WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+ return true;
+ }
+ });
+ return config;
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java
index 41e4e46..f7b3bf6 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiNotificationControllerTest.java
@@ -26,14 +26,16 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
@@ -54,9 +56,12 @@
public static final String TAG = "WifiScanningServiceTest";
@Mock private Context mContext;
- @Mock private WifiStateMachine mWifiStateMachine;
+ @Mock private Resources mResources;
@Mock private FrameworkFacade mFrameworkFacade;
@Mock private NotificationManager mNotificationManager;
+ @Mock private UserManager mUserManager;
+ @Mock private WifiInjector mWifiInjector;
+ @Mock private WifiScanner mWifiScanner;
WifiNotificationController mWifiNotificationController;
/**
@@ -69,20 +74,22 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
-
- // Needed for the NotificationEnabledSettingObserver.
- when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
-
when(mContext.getSystemService(Context.NOTIFICATION_SERVICE))
.thenReturn(mNotificationManager);
when(mFrameworkFacade.getIntegerSetting(mContext,
Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1)).thenReturn(1);
- MockLooper mock_looper = new MockLooper();
+ when(mContext.getSystemService(Context.USER_SERVICE))
+ .thenReturn(mUserManager);
+ when(mContext.getResources()).thenReturn(mResources);
+
+ when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
+
+ TestLooper mock_looper = new TestLooper();
mWifiNotificationController = new WifiNotificationController(
- mContext, mock_looper.getLooper(), mWifiStateMachine, mFrameworkFacade,
- mock(Notification.Builder.class));
+ mContext, mock_looper.getLooper(), mFrameworkFacade,
+ mock(Notification.Builder.class), mWifiInjector);
ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
@@ -96,7 +103,7 @@
ScanResult scanResult = new ScanResult();
scanResult.capabilities = "[ESS]";
scanResults.add(scanResult);
- when(mWifiStateMachine.syncGetScanResultsList()).thenReturn(scanResults);
+ when(mWifiScanner.getSingleScanResults()).thenReturn(scanResults);
}
/** Verifies that a notification is displayed (and retracted) given system events. */
@@ -111,8 +118,7 @@
TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
verify(mNotificationManager, never())
- .notifyAsUser(any(String.class), anyInt(), any(Notification.class),
- any(UserHandle.class));
+ .notifyAsUser(any(), anyInt(), any(), any(UserHandle.class));
// Changing to and from "SCANNING" state should not affect the counter.
TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
@@ -120,21 +126,36 @@
TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
NetworkInfo.DetailedState.DISCONNECTED);
- // Needed while WifiNotificationController creates its notification.
- when(mContext.getResources()).thenReturn(mock(Resources.class));
-
// The third scan result notification will trigger the notification.
TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
verify(mNotificationManager)
- .notifyAsUser(any(String.class), anyInt(), any(Notification.class),
- any(UserHandle.class));
+ .notifyAsUser(any(), anyInt(), any(), any(UserHandle.class));
verify(mNotificationManager, never())
- .cancelAsUser(any(String.class), anyInt(), any(UserHandle.class));
+ .cancelAsUser(any(), anyInt(), any(UserHandle.class));
// Changing network state should cause the notification to go away.
TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
NetworkInfo.DetailedState.CONNECTED);
verify(mNotificationManager)
- .cancelAsUser(any(String.class), anyInt(), any(UserHandle.class));
+ .cancelAsUser(any(), anyInt(), any(UserHandle.class));
+ }
+
+ @Test
+ public void verifyNotificationNotDisplayed_userHasDisallowConfigWifiRestriction() {
+ when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT))
+ .thenReturn(true);
+
+ TestUtil.sendWifiStateChanged(mBroadcastReceiver, mContext, WifiManager.WIFI_STATE_ENABLED);
+ TestUtil.sendNetworkStateChanged(mBroadcastReceiver, mContext,
+ NetworkInfo.DetailedState.DISCONNECTED);
+ setOpenAccessPoint();
+
+ // The notification should be displayed after three scan results.
+ TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
+ TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
+ TestUtil.sendScanResultsAvailable(mBroadcastReceiver, mContext);
+
+ verify(mNotificationManager, never())
+ .notifyAsUser(any(), anyInt(), any(), any(UserHandle.class));
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java b/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java
deleted file mode 100644
index c97618d..0000000
--- a/tests/wifitests/src/com/android/server/wifi/WifiQualifiedNetworkSelectorTest.java
+++ /dev/null
@@ -1,2685 +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 com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_EAP;
-import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE;
-import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK;
-import static com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import org.mockito.AdditionalAnswers;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.validateMockitoUsage;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.NetworkScoreManager;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiEnterpriseConfig;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiSsid;
-import android.os.SystemClock;
-import android.os.UserManager;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.LocalLog;
-import com.android.server.wifi.NetworkUpdateResult;
-
-import com.android.internal.R;
-import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
-import com.android.server.wifi.util.ScanDetailUtil;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Unit tests for {@link com.android.server.wifi.WifiQualifiedNetworkSelector}.
- */
-@SmallTest
-public class WifiQualifiedNetworkSelectorTest {
-
- @Before
- public void setUp() throws Exception {
- mResource = getResource();
- mScoreManager = getNetworkScoreManager();
- mScoreCache = getScoreCache();
- mContext = getContext();
- mWifiConfigManager = getWifiConfigManager();
- mWifiInfo = getWifiInfo();
- mLocalLog = getLocalLog();
-
- mWifiQualifiedNetworkSelector = new WifiQualifiedNetworkSelector(mWifiConfigManager,
- mContext, mWifiInfo, mClock);
- mWifiQualifiedNetworkSelector.enableVerboseLogging(1);
- mWifiQualifiedNetworkSelector.setUserPreferredBand(1);
- mWifiQualifiedNetworkSelector.setWifiNetworkScoreCache(mScoreCache);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
-
- //setup Carrier Networks
- int eapType = 4;
-
- WifiConfiguration wifiConfig = new WifiConfiguration();
- wifiConfig.SSID = "\"TEST1\"";
- wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
- wifiConfig.enterpriseConfig = new WifiEnterpriseConfig();
- wifiConfig.enterpriseConfig.setEapMethod(eapType);
- mCarrierConfiguredNetworks.add(wifiConfig);
-
- WifiConfiguration wifiConfig1 = new WifiConfiguration();
- wifiConfig1.SSID = "\"TEST2\"";
- wifiConfig1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
- wifiConfig1.enterpriseConfig = new WifiEnterpriseConfig();
- wifiConfig1.enterpriseConfig.setEapMethod(eapType);
- mCarrierConfiguredNetworks.add(wifiConfig1);
-
- WifiConfiguration wifiConfig2 = new WifiConfiguration();
- wifiConfig2.SSID = "\"TEST3\"";
- wifiConfig2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
- wifiConfig2.enterpriseConfig = new WifiEnterpriseConfig();
- wifiConfig2.enterpriseConfig.setEapMethod(eapType);
- mCarrierConfiguredNetworks.add(wifiConfig2);
- mWifiQualifiedNetworkSelector.setCarrierConfiguredNetworks(mCarrierConfiguredNetworks);
- }
-
- @After
- public void cleanup() {
- validateMockitoUsage();
- }
-
- private WifiQualifiedNetworkSelector mWifiQualifiedNetworkSelector = null;
- private WifiConfigManager mWifiConfigManager = null;
- private Context mContext;
- private Resources mResource;
- private NetworkScoreManager mScoreManager;
- private WifiNetworkScoreCache mScoreCache;
- private WifiInfo mWifiInfo;
- private LocalLog mLocalLog;
- private Clock mClock = mock(Clock.class);
- private static final String[] DEFAULT_SSIDS = {"\"test1\"", "\"test2\""};
- private static final String[] DEFAULT_BSSIDS = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
- private static final String TAG = "QNS Unit Test";
- List<WifiConfiguration> mCarrierConfiguredNetworks = new ArrayList<WifiConfiguration>();
-
- private List<ScanDetail> getScanDetails(String[] ssids, String[] bssids, int[] frequencies,
- String[] caps, int[] levels) {
- List<ScanDetail> scanDetailList = new ArrayList<ScanDetail>();
- long timeStamp = mClock.elapsedRealtime();
- 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);
- scanDetailList.add(scanDetail);
- }
- return scanDetailList;
- }
-
- Context getContext() {
- Context context = mock(Context.class);
- Resources resource = mock(Resources.class);
-
- when(context.getResources()).thenReturn(mResource);
- when(context.getSystemService(Context.NETWORK_SCORE_SERVICE)).thenReturn(mScoreManager);
- return context;
- }
-
- Resources getResource() {
- Resources resource = mock(Resources.class);
-
- when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80);
- when(resource.getInteger(R.integer.config_wifi_framework_RSSI_SCORE_OFFSET)).thenReturn(85);
- when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24);
- when(resource.getInteger(R.integer.config_wifi_framework_LAST_SELECTION_AWARD))
- .thenReturn(480);
- when(resource.getInteger(R.integer.config_wifi_framework_PASSPOINT_SECURITY_AWARD))
- .thenReturn(40);
- when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80);
- when(resource.getInteger(R.integer.config_wifi_framework_RSSI_SCORE_SLOPE)).thenReturn(4);
- return resource;
- }
-
- NetworkScoreManager getNetworkScoreManager() {
- NetworkScoreManager networkScoreManager = mock(NetworkScoreManager.class);
-
- return networkScoreManager;
- }
-
- WifiNetworkScoreCache getScoreCache() {
- return mock(WifiNetworkScoreCache.class);
- }
-
- LocalLog getLocalLog() {
- return new LocalLog(0);
- }
-
- WifiInfo getWifiInfo() {
- WifiInfo wifiInfo = mock(WifiInfo.class);
-
- //simulate a disconnected state
- when(wifiInfo.is24GHz()).thenReturn(true);
- when(wifiInfo.is5GHz()).thenReturn(false);
- when(wifiInfo.getRssi()).thenReturn(-70);
- when(wifiInfo.getNetworkId()).thenReturn(WifiConfiguration.INVALID_NETWORK_ID);
- when(wifiInfo.getBSSID()).thenReturn(null);
- when(wifiInfo.getNetworkId()).thenReturn(-1);
- return wifiInfo;
- }
-
- WifiConfigManager getWifiConfigManager() {
- WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class);
- wifiConfigManager.mThresholdSaturatedRssi24 = new AtomicInteger(
- WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND);
- wifiConfigManager.mBandAward5Ghz = new AtomicInteger(
- WifiQualifiedNetworkSelector.BAND_AWARD_5GHz);
- wifiConfigManager.mCurrentNetworkBoost = new AtomicInteger(
- WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD);
- wifiConfigManager.mThresholdQualifiedRssi5 = new AtomicInteger(
- WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND);
- wifiConfigManager.mThresholdMinimumRssi24 = new AtomicInteger(
- WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI);
- wifiConfigManager.mThresholdMinimumRssi5 = new AtomicInteger(
- WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI);
-
- when(wifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- return wifiConfigManager;
- }
-
- /**
- * This API is used to generate multiple simulated saved configurations used for test
- *
- * @param ssid array of SSID of saved configuration
- * @param security array of securities of saved configuration
- * @return generated new array of configurations based on input
- */
- private WifiConfiguration[] generateWifiConfigurations(String[] ssid, int[] security) {
- if (ssid == null || security == null || ssid.length != security.length
- || ssid.length == 0) {
- return null;
- }
-
- WifiConfiguration[] configs = new WifiConfiguration[ssid.length];
- for (int index = 0; index < ssid.length; index++) {
- configs[index] = generateWifiConfig(index, 0, ssid[index], false, true, null, null,
- security[index]);
- }
-
- return configs;
- }
-
- /**
- * set configuration to a passpoint configuration
- *
- * @param config The configuration need to be set as a passipoint configuration
- */
- private void setConfigPasspoint(WifiConfiguration config) {
- config.FQDN = "android.qns.unitTest";
- config.providerFriendlyName = "android.qns.unitTest";
- WifiEnterpriseConfig enterpriseConfig = mock(WifiEnterpriseConfig.class);
- when(enterpriseConfig.getEapMethod()).thenReturn(WifiEnterpriseConfig.Eap.PEAP);
-
- }
-
- /**
- * add the Configurations to WifiConfigManager (WifiConfigureStore can take them out according
- * to the networkd ID)
- *
- * @param configs input configuration need to be added to WifiConfigureStore
- */
- private void prepareConfigStore(final WifiConfiguration[] configs) {
- when(mWifiConfigManager.getWifiConfiguration(anyInt()))
- .then(new AnswerWithArguments() {
- public WifiConfiguration answer(int netId) {
- if (netId >= 0 && netId < configs.length) {
- return configs[netId];
- } else {
- return null;
- }
- }
- });
- }
-
- /**
- * Link scan results to the saved configurations.
- *
- * The shorter of the 2 input params will be used to loop over so the inputs don't
- * need to be of equal length. If there are more scan details then configs the remaining scan
- * details will be associated with a NULL config.
- *
- * @param configs saved configurations
- * @param scanDetails come in scan results
- */
- private void scanResultLinkConfiguration(WifiConfiguration[] configs,
- List<ScanDetail> scanDetails) {
- if (scanDetails.size() <= configs.length) {
- for (int i = 0; i < scanDetails.size(); i++) {
- ScanDetail scanDetail = scanDetails.get(i);
- List<WifiConfiguration> associateWithScanResult = new ArrayList<>();
- associateWithScanResult.add(configs[i]);
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(eq(scanDetail),
- anyBoolean())).thenReturn(associateWithScanResult);
- }
- } else {
- for (int i = 0; i < configs.length; i++) {
- ScanDetail scanDetail = scanDetails.get(i);
- List<WifiConfiguration> associateWithScanResult = new ArrayList<>();
- associateWithScanResult.add(configs[i]);
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(eq(scanDetail),
- anyBoolean())).thenReturn(associateWithScanResult);
- }
-
- // associated the remaining scan details with a NULL config.
- for (int i = configs.length; i < scanDetails.size(); i++) {
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(eq(scanDetails.get(i)),
- anyBoolean())).thenReturn(null);
- }
- }
- }
-
- private void configureScoreCache(List<ScanDetail> scanDetails, Integer[] scores,
- boolean[] meteredHints) {
- for (int i = 0; i < scanDetails.size(); i++) {
- ScanDetail scanDetail = scanDetails.get(i);
- Integer score = scores[i];
- ScanResult scanResult = scanDetail.getScanResult();
- if (score != null) {
- when(mScoreCache.isScoredNetwork(scanResult)).thenReturn(true);
- when(mScoreCache.hasScoreCurve(scanResult)).thenReturn(true);
- when(mScoreCache.getNetworkScore(eq(scanResult), anyBoolean())).thenReturn(score);
- when(mScoreCache.getNetworkScore(scanResult)).thenReturn(score);
- } else {
- when(mScoreCache.isScoredNetwork(scanResult)).thenReturn(false);
- when(mScoreCache.hasScoreCurve(scanResult)).thenReturn(false);
- when(mScoreCache.getNetworkScore(eq(scanResult), anyBoolean())).thenReturn(
- WifiNetworkScoreCache.INVALID_NETWORK_SCORE);
- when(mScoreCache.getNetworkScore(scanResult)).thenReturn(
- WifiNetworkScoreCache.INVALID_NETWORK_SCORE);
- }
- when(mScoreCache.getMeteredHint(scanResult)).thenReturn(meteredHints[i]);
- }
- }
-
- /**
- * verify whether the chosen configuration matched with the expected chosen scan result
- *
- * @param chosenScanResult the expected chosen scan result
- * @param candidate the chosen configuration
- */
- private void verifySelectedResult(ScanResult chosenScanResult, WifiConfiguration candidate) {
- ScanResult candidateScan = candidate.getNetworkSelectionStatus().getCandidate();
- assertEquals("choose the wrong SSID", chosenScanResult.SSID, candidate.SSID);
- assertEquals("choose the wrong BSSID", chosenScanResult.BSSID, candidateScan.BSSID);
- }
-
- // QNS test under disconnected State
-
- /**
- * Case #1 choose 2GHz stronger RSSI test
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network
- * Both network are enabled, encrypted and at 2.4 GHz
- * test1 is with RSSI -70 test2 is with RSSI -60
- *
- * Expected behavior: test2 is chosen
- */
- @Test
- public void chooseNetworkDisconnected2GHighestRssi() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 2417};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-70, -60};
- 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(scanDetails.size() - 1).getScanResult();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #2 choose 5GHz Stronger RSSI Test
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network
- * Both network are enabled, encrypted and at 5 GHz
- * test1 is with RSSI -70 test2 is with RSSI -60
- *
- * Expected behavior: test2 is chosen
- */
- @Test
- public void chooseNetworkDisconnected5GHighestRssi() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {5180, 5610};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-70, -60};
- 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(scanDetails.size() - 1).getScanResult();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #3 5GHz over 2GHz bonus Test
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network
- * Both network are enabled
- * test1 is @ 2GHz with RSSI -60
- * test2 is @ 5Ghz with RSSI -65
- *
- * Expected behavior: test2 is chosen due to 5GHz bonus
- */
- @Test
- public void chooseNetworkDisconnect5GOver2GTest() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-60, -65};
- 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(scanDetails.size() - 1).getScanResult();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #4 2GHz over 5GHz dur to 5GHz signal too weak test
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network
- * Both network are enabled
- * test1 is @ 2GHz with RSSI -60
- * test2 is @ 5Ghz with RSSI -75
- *
- * Expected behavior: test1 is chosen due to 5GHz signal is too weak (5GHz bonus can not
- * compensate)
- */
- @Test
- public void chooseNetworkDisconnect2GOver5GTest() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-60, -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(0).getScanResult();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #5 2GHz signal Saturation test
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network
- * Both network are enabled
- * test1 is @ 2GHz with RSSI -50
- * test2 is @ 5Ghz with RSSI -65
- *
- * Expected behavior: test2 is chosen. Although the RSSI delta here is 15 too, because 2GHz RSSI
- * saturates at -60, the real RSSI delta is only 5, which is less than 5GHz bonus
- */
- @Test
- public void chooseNetworkDisconnect2GRssiSaturationTest() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-50, -65};
- 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(scanDetails.size() - 1).getScanResult();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #6 Minimum RSSI test
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network
- * Both network are enabled
- * test1 is @ 2GHz with RSSI -86
- * test2 is @ 5Ghz with RSSI -83
- *
- * Expected behavior: no QNS is made because both network are below the minimum threshold, null
- */
- @Test
- public void chooseNetworkMinimumRssiTest() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI - 1,
- WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI - 1};
- int[] security = {SECURITY_EAP, 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);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- assertEquals("choose the wrong SSID", null, candidate);
- }
-
- /**
- * Case #7 encrypted network over passpoint network
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * Two networks test1 is secured network, test2 are passpoint network
- * Both network are enabled and at 2.4 GHz. Both have RSSI of -70
- *
- * Expected behavior: test1 is chosen since secured network has higher priority than passpoint
- * network
- */
- @Test
- public void chooseNetworkSecurityOverPassPoint() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
- int[] levels = {-70, -70};
- int[] security = {SECURITY_EAP, SECURITY_NONE};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security);
- setConfigPasspoint(savedConfigs[1]);
- prepareConfigStore(savedConfigs);
-
- final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
- ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #8 passpoint network over open network
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * Two networks test1 is passpoint network, test2 is open network
- * Both network are enabled and at 2.4 GHz. Both have RSSI of -70
- *
- * Expected behavior: test1 is chosen since passpoint network has higher priority than open
- * network
- */
- @Test
- public void chooseNetworkPasspointOverOpen() {
- String[] ssids = {"\"test1\"", "\"test2\""};
- String[] bssids = {"6c:f3:7f:ae:8c:f8", "6c:f3:7f:ae:8c:f4"};
- int[] frequencies = {2437, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-70, -70};
- int[] security = {SECURITY_NONE, SECURITY_NONE};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security);
- setConfigPasspoint(savedConfigs[0]);
- prepareConfigStore(savedConfigs);
-
- final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
- ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #9 secure network over open network
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * Two networks test1 is secure network, test2 is open network
- * Both network are enabled and at 2.4 GHz. Both have RSSI of -70
- *
- * Expected behavior: test1 is chosen since secured network has higher priority than open
- * network
- */
- @Test
- public void chooseNetworkSecureOverOpen() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-70, -70};
- int[] security = {SECURITY_PSK, SECURITY_NONE};
-
- 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();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #10 first time user select a network
- *
- * In this test. we simulate following scenario
- * There are three saved networks: test1, test2 and test3. Now user select the network test3
- * check test3 has been saved in test1's and test2's ConnectChoice
- *
- * Expected behavior: test1's and test2's ConnectChoice should be test3, test3's ConnectChoice
- * should be null
- */
- @Test
- public void userSelectsNetworkForFirstTime() {
- String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""};
- int[] security = {SECURITY_PSK, SECURITY_PSK, SECURITY_NONE};
-
- final WifiConfiguration[] configs = generateWifiConfigurations(ssids, security);
- prepareConfigStore(configs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(configs));
- for (WifiConfiguration network : configs) {
- WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus();
- status.setSeenInLastQualifiedNetworkSelection(true);
- }
-
- mWifiQualifiedNetworkSelector.userSelectNetwork(configs.length - 1, true);
- String key = configs[configs.length - 1].configKey();
- for (int index = 0; index < configs.length; index++) {
- WifiConfiguration config = configs[index];
- WifiConfiguration.NetworkSelectionStatus status = config.getNetworkSelectionStatus();
- if (index == configs.length - 1) {
- assertEquals("User selected network should not have prefernce over it", null,
- status.getConnectChoice());
- } else {
- assertEquals("Wrong user preference", key, status.getConnectChoice());
- }
- }
- }
-
- /**
- * Case #11 choose user selected network
- *
- * In this test, we simulate following scenario:
- * WifiStateMachine is under disconnected state
- * There are three networks: test1, test2, test3 and test3 is the user preference
- * All three networks are enabled
- * test1 is @ 2.4GHz with RSSI -50 PSK
- * test2 is @ 5Ghz with RSSI -65 PSK
- * test3 is @ 2.4GHz with RSSI -55 open
- *
- * Expected behavior: test3 is chosen since it is user selected network. It overcome all other
- * priorities
- */
- @Test
- public void chooseUserPreferredNetwork() {
- String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""};
- int[] security = {SECURITY_PSK, SECURITY_PSK, SECURITY_NONE};
-
- final WifiConfiguration[] configs = generateWifiConfigurations(ssids, security);
- prepareConfigStore(configs);
- for (WifiConfiguration network : configs) {
- WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus();
- status.setSeenInLastQualifiedNetworkSelection(true);
- }
-
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(configs));
-
- //set user preference
- mWifiQualifiedNetworkSelector.userSelectNetwork(ssids.length - 1, true);
- //Generate mocked recent scan results
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"};
- int[] frequencies = {2437, 5180, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]", "NONE"};
- int[] levels = {-50, -65, -55};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- scanResultLinkConfiguration(configs, scanDetails);
-
- ScanResult chosenScanResult = scanDetails.get(scanDetails.size() - 1).getScanResult();
- when(mWifiConfigManager.getWifiConfiguration(configs[2].configKey()))
- .thenReturn(configs[2]);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #12 enable a blacklisted BSSID
- *
- * In this test, we simulate following scenario:
- * For two Aps, BSSIDA and BSSIDB. Disable BSSIDA, then check whether BSSIDA is disabled and
- * BSSIDB is enabled. Then enable BSSIDA, check whether both BSSIDs are enabled.
- */
- @Test
- public void enableBssidTest() {
- String bssidA = "6c:f3:7f:ae:8c:f3";
- String bssidB = "6c:f3:7f:ae:8c:f4";
- //check by default these two BSSIDs should be enabled
- assertEquals("bssidA should be enabled by default",
- mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), false);
- assertEquals("bssidB should be enabled by default",
- mWifiQualifiedNetworkSelector.isBssidDisabled(bssidB), false);
-
- //disable bssidA 3 times, check whether A is dsiabled and B is still enabled
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssidA, false);
- assertEquals("bssidA should be disabled",
- mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), false);
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssidA, false);
- assertEquals("bssidA should be disabled",
- mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), false);
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssidA, false);
- assertEquals("bssidA should be disabled",
- mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), true);
- assertEquals("bssidB should still be enabled",
- mWifiQualifiedNetworkSelector.isBssidDisabled(bssidB), false);
-
- //re-enable bssidA, check whether A is dsiabled and B is still enabled
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssidA, true);
- assertEquals("bssidA should be enabled by default",
- mWifiQualifiedNetworkSelector.isBssidDisabled(bssidA), false);
- assertEquals("bssidB should be enabled by default",
- mWifiQualifiedNetworkSelector.isBssidDisabled(bssidB), false);
-
- //make sure illegal input will not cause crash
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(null, false);
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(null, true);
- }
-
- /**
- * Case #13 do not choose the BSSID has been disabled
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network and found in scan results
- * Both network are enabled
- * test1 is @ 2GHz with RSSI -65
- * test2 is @ 5Ghz with RSSI -50
- * test2's BSSID is disabled
- *
- * expected return test1 since test2's BSSID has been disabled
- */
- @Test
- public void networkChooseWithOneBssidDisabled() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-65, -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.enableBssidForQualityNetworkSelection(bssids[1], false);
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false);
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false);
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #14 re-choose the disabled BSSID after it is re-enabled
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network and found in scan results
- * Both network are enabled
- * test1 is @ 2GHz with RSSI -65
- * test2 is @ 5Ghz with RSSI -50
- * test2's BSSID is disabled
- *
- * expected return test2 since test2's BSSID has been enabled again
- */
- @Test
- public void networkChooseWithOneBssidReenaabled() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-65, -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.enableBssidForQualityNetworkSelection(bssids[1], false);
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false);
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false);
- //re-enable it
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], true);
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #15 re-choose the disabled BSSID after its disability has expired
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network and found in scan results
- * Both network are enabled
- * test1 is @ 2GHz with RSSI -65
- * test2 is @ 5Ghz with RSSI -50
- * test2's BSSID is disabled
- *
- * expected return test2 since test2's BSSID has been enabled again
- */
- @Test
- public void networkChooseWithOneBssidDisableExpire() {
- String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\""};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "6c:f3:7f:ae:8c:f5"};
- int[] frequencies = {2437, 5180, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]",
- "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-65, -50, -60};
- int[] security = {SECURITY_PSK, 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();
-
- for (int index = 0; index < WifiQualifiedNetworkSelector.BSSID_BLACKLIST_THRESHOLD;
- index++) {
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[1], false);
- mWifiQualifiedNetworkSelector.enableBssidForQualityNetworkSelection(bssids[2], false);
- }
-
- //re-enable it
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime()
- + WifiQualifiedNetworkSelector.BSSID_BLACKLIST_EXPIRE_TIME);
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
- /**
- * Case #16 do not choose the SSID has been disabled
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under disconnected state
- * Two networks test1, test2 are secured network and found in scan results
- * Both network are enabled
- * test1 is @ 2GHz with RSSI -65
- * test2 is @ 5Ghz with RSSI -50
- * test2's SSID is disabled
- *
- * expected return test1 since test2's SSID has been disabled
- */
- @Test
- public void networkChooseWithOneSsidDisabled() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-65, -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();
-
- when(mWifiConfigManager.tryEnableQualifiedNetwork(anyInt())).thenReturn(true);
- savedConfigs[1].getNetworkSelectionStatus().setNetworkSelectionStatus(
- WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #17 do not make QNS is link is bouncing now
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under disconnected state and currently is under link bouncing
- * Two networks test1, test2 are secured network and found in scan results
- * Both network are enabled
- * test1 is @ 2GHz with RSSI -50
- * test2 is @ 5Ghz with RSSI -50
- *
- * expected return null
- */
- @Test
- public void noQNSWhenLinkBouncingDisconnected() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {WifiQualifiedNetworkSelector.MINIMUM_2G_ACCEPT_RSSI - 1,
- WifiQualifiedNetworkSelector.MINIMUM_5G_ACCEPT_RSSI - 1};
- 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);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, true, false, true, false);
-
- assertEquals("choose the wrong network", null, candidate);
- }
-
- /**
- * Case #18 QNS with very short gap
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under disconnected state
- * If last QNS is made in less than MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL, we
- * still should make new QNS since it is disconnected now
- *
- * expect return test1 because of band bonus
- */
- @Test
- public void networkSelectionInShortGap() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-50, -65};
- 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);
- //first QNS
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
- //immediately second QNS
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
- ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- //Unit test for Connected State
-
- /**
- * Case #19 no QNS with very short gap when connected
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and test2 is connected
- * When WifiStateMachine is already in connected state, if last QNS is made in less than
- * MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL, no new QNS should be made
- *
- * expect return NULL
- */
- @Test
- public void noNetworkSelectionDueToShortGap() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-50, -65};
- 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);
- //first QNS
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
- //immediately second QNS
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
- ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
- assertEquals("choose the wrong BSSID", null, candidate);
- }
-
- /**
- * Case #20 force QNS with very short gap under connection
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and test2 is connected
- * When WifiStateMachine is already in connected state, if last QNS is made in less than
- * MINIMUM_QUALIFIED_NETWORK_SELECTION_INTERVAL, no new QNS should be made. However, if we force
- * to make new QNS, QNS still will be made
- *
- * expect return test2 since it is the current connected one (bonus)
- */
- @Test
- public void forceNetworkSelectionInShortGap() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-50, -65};
- 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);
- //first QNS
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
- when(mWifiInfo.getNetworkId()).thenReturn(1);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
- when(mWifiInfo.is24GHz()).thenReturn(false);
- when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- //immediately second QNS
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(true,
- false, scanDetails, false, true, false, false);
- ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #21 no QNS when connected and user do not allow switch when connected
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and test2 is connected
- * if user does not allow switch network when connected, do not make new QNS when connected
- *
- * expect return NULL
- */
- @Test
- public void noNewNetworkSelectionDuetoUserDisableSwitchWhenConnected() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-50, -65};
- 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);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
- assertEquals("choose the wrong BSSID", null, candidate);
- assertEquals("Should receive zero filteredScanDetails", 0,
- mWifiQualifiedNetworkSelector.getFilteredScanDetails().size());
- }
-
- /**
- * Case #22 no new QNS if current network is qualified already
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and test2 is connected
- * If current connected network is Qualified already, do not make new QNS
- * simulated current connected network as:
- * 5GHz, RSSI = WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND, secured
- *
- * expected return null
- */
- @Test
- public void noNewQNSCurrentNetworkQualified() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-65, WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND};
- 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);
-
- //first time, connect to test2 due to 5GHz bonus
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
- 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);
-
- 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);
- assertEquals("choose the wrong BSSID", null, candidate);
- }
-
- /**
- * Case #23 No new QNS when link bouncing when connected
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and test2 is connected
- * no new QNS when link is bouncing
- *
- * expected return null
- */
- @Test
- public void noNewQNSLinkBouncing() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 5180};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-70, -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);
-
- //first connect to test2 due to 5GHz bonus
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
- 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(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, true, true, false, false);
- assertEquals("choose the wrong BSSID", null, candidate);
- }
-
- /**
- * Case #24 Qualified network need to be on 5GHz
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and connected to test2
- * if current connected network is not 5GHz, then it is not qualified. We should make new QNS
- *
- * expected result: return test1
- */
- @Test
- public void currentNetworkNotQualifiedDueToBandMismatch() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-50, -65};
- 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);
- when(mWifiInfo.getNetworkId()).thenReturn(0);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
- when(mWifiInfo.is24GHz()).thenReturn(true);
- //connect to config2 first
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
-
- when(mClock.elapsedRealtime()).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);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #25 Qualified network need to be secured
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and current connects to test2
- * if current connected network is open network, then it is not qualified. We should make new
- * QNS
- *
- * expected result: return test1 since test1 has higher RSSI
- */
- @Test
- public void currentNetworkNotQualifiedDueToOpenNetwork() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {5400, 5400};
- String[] caps = {"[ESS]", "[ESS]"};
- int[] levels = {-70, -65};
- int[] security = {SECURITY_NONE, SECURITY_NONE};
-
- 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);
-
- //first connect to test2 because of RSSI
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
- 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(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- 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);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #26 ephemeral network can not be qualified network
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and current connected to test2
- * if current connected network is ephemeral network, then it is not qualified. We should make
- * new QNS
- *
- * expected result: return test1 (since test2 is ephemeral)
- */
- @Test
- public void currentNetworkNotQualifiedDueToEphemeral() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {5200, 5200};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-100, -50};
- int[] security = {SECURITY_PSK, SECURITY_PSK};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security);
- savedConfigs[1].ephemeral = true;
- prepareConfigStore(savedConfigs);
-
- final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
-
- //first connect to test2 since test1's RSSI is negligible
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
- when(mWifiInfo.getNetworkId()).thenReturn(1);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
- when(mWifiInfo.is24GHz()).thenReturn(false);
-
- levels[0] = -70;
- scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
- when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
-
- ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #27 low signal network can not be Qualified network (5GHz)
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and current connected to test2
- * if current connected network's rssi is too low, then it is not qualified. We should
- * make new QNS
- *
- * expected result: return test1
- */
- @Test
- public void currentNetworkNotQualifiedDueToLow5GRssi() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {5200, 5200};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-80, WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND - 1};
- 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);
-
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
- when(mWifiInfo.getNetworkId()).thenReturn(1);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
- when(mWifiInfo.getRssi()).thenReturn(levels[1]);
- when(mWifiInfo.is24GHz()).thenReturn(false);
- when(mWifiInfo.is5GHz()).thenReturn(true);
-
- when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- when(mClock.elapsedRealtime()).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);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #28 low signal network can not be Qualified network (2.4GHz)
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and current connected to test2
- * if current connected network's rssi is too low, then it is not qualified. We should
- * make new QNS
- *
- * expected result: return test1
- */
- @Test
- public void currentNetworkNotQualifiedDueToLow2GRssi() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-100, WifiQualifiedNetworkSelector.QUALIFIED_RSSI_24G_BAND - 1};
- 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);
-
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
-
- when(mWifiInfo.getNetworkId()).thenReturn(1);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
- when(mWifiInfo.getRssi()).thenReturn(levels[1]);
- when(mWifiInfo.is24GHz()).thenReturn(false);
- when(mWifiInfo.is5GHz()).thenReturn(true);
-
- when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- levels[0] = -60;
- scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- when(mClock.elapsedRealtime()).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);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #29 Choose current network due to current network bonus
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and current connected to test2
- * To connect to a network which is not linked to current connected network, unless this network
- * is more than 10 db higher than current network, we should not switch. So although test2 has a
- * lower signal, we still choose test2
- *
- * expected result: return test2
- */
- @Test
- public void currentNetworkStayDueToSameNetworkBonus() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-100, -80};
- 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);
-
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
- when(mWifiInfo.getNetworkId()).thenReturn(1);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
- when(mWifiInfo.is24GHz()).thenReturn(true);
-
- when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- 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);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
-
- ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #30 choose another network due to current network's signal is too low
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and current connected to test2
- * To connect to a network which is not linked to current connected network, if this network
- * is more than 10 db higher than current network, we should switch
- *
- * expected new result: return test1
- */
- @Test
- public void switchNetworkStayDueToCurrentNetworkRssiLow() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-100, -80};
- 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);
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
-
- when(mWifiInfo.getNetworkId()).thenReturn(1);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
- when(mWifiInfo.is24GHz()).thenReturn(true);
-
- when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- 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);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
-
- ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #31 Choose current BSSID due to current BSSID bonus
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and current connected to test2
- * Linked network will be treated as same network. To connect to a network which is linked to
- * current connected network, unless this network is more than 6 db higher than current network,
- * we should not switch AP and stick to current BSSID
- *
- * expected result: return test2
- */
- @Test
- public void currentBssidStayDueToSameBSSIDBonus() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-100, -80};
- int[] security = {SECURITY_PSK, SECURITY_PSK};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security);
- //link two configuration
- savedConfigs[0].linkedConfigurations = new HashMap<String, Integer>();
- savedConfigs[1].linkedConfigurations = new HashMap<String, Integer>();
- savedConfigs[0].linkedConfigurations.put(savedConfigs[1].configKey(), 1);
- savedConfigs[1].linkedConfigurations.put(savedConfigs[0].configKey(), 1);
- prepareConfigStore(savedConfigs);
-
- final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
-
- when(mWifiInfo.getNetworkId()).thenReturn(1);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
- when(mWifiInfo.is24GHz()).thenReturn(true);
-
- 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);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
-
- ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #32 Choose another BSSID due to current BSSID's rssi is too low
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and current connected to test2
- * Linked network will be treated as same network. To connect to a network which is linked to
- * current connected network, unless this network is more than 6 db higher than current network,
- * we should not switch AP and stick to current BSSID
- *
- * expected result: return test2
- */
- @Test
- public void swithBssidDueToLowRssi() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {2437, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS][ESS]"};
- int[] levels = {-100, -80};
- int[] security = {SECURITY_PSK, SECURITY_PSK};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security);
- //link two configuration
- savedConfigs[0].linkedConfigurations = new HashMap<String, Integer>();
- savedConfigs[1].linkedConfigurations = new HashMap<String, Integer>();
- savedConfigs[0].linkedConfigurations.put(savedConfigs[1].configKey(), 1);
- savedConfigs[1].linkedConfigurations.put(savedConfigs[0].configKey(), 1);
- prepareConfigStore(savedConfigs);
-
- final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
-
- when(mWifiInfo.getNetworkId()).thenReturn(1);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
- when(mWifiInfo.is24GHz()).thenReturn(true);
-
- 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);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
-
- ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #33 Choose an ephemeral network with a good score because no saved networks
- * are available.
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is not connected to any network.
- * selectQualifiedNetwork() is called with 2 scan results, test1 and test2.
- * test1 is an enterprise network w/o a score.
- * test2 is an open network with a good score. Additionally it's a metered network.
- * isUntrustedConnectionsAllowed is set to true.
- *
- * expected result: return test2 with meteredHint set to True.
- */
- @Test
- public void selectQualifiedNetworkChoosesEphemeral() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {5200, 5200};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
- int[] levels = {-70, -70};
- Integer[] scores = {null, 120};
- boolean[] meteredHints = {false, true};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- configureScoreCache(scanDetails, scores, meteredHints);
-
- // No saved networks.
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(any(ScanDetail.class),
- anyBoolean())).thenReturn(null);
-
- WifiConfiguration unTrustedNetworkCandidate = mock(WifiConfiguration.class);
- // Setup the config as an invalid candidate. This is done to workaround a Mockito issue.
- // Basically Mockito is unable to mock package-private methods in classes loaded from a
- // different Jar (like all of the framework code) which results in the actual saveNetwork()
- // method being invoked in this case. Because the config is invalid it quickly returns.
- unTrustedNetworkCandidate.SSID = null;
- unTrustedNetworkCandidate.networkId = WifiConfiguration.INVALID_NETWORK_ID;
- ScanResult untrustedScanResult = scanDetails.get(1).getScanResult();
- when(mWifiConfigManager
- .wifiConfigurationFromScanResult(untrustedScanResult))
- .thenReturn(unTrustedNetworkCandidate);
-
- WifiConfiguration.NetworkSelectionStatus selectionStatus =
- mock(WifiConfiguration.NetworkSelectionStatus.class);
- when(unTrustedNetworkCandidate.getNetworkSelectionStatus()).thenReturn(selectionStatus);
- when(selectionStatus.getCandidate()).thenReturn(untrustedScanResult);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
- false /* forceSelectNetwork */,
- true /* isUntrustedConnectionsAllowed */,
- scanDetails,
- false, /* isLinkDebouncing */
- false, /* isConnected */
- true, /* isDisconnected */
- false /* isSupplicantTransient */);
- verify(selectionStatus).setCandidate(untrustedScanResult);
- assertSame(unTrustedNetworkCandidate, candidate);
- assertEquals(meteredHints[1], candidate.meteredHint);
- }
-
- /**
- * Case #34 Test Filtering of potential candidate scanDetails (Untrusted allowed)
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * test1 is @ 2GHz with RSSI -60
- * test2 is @ 5Ghz with RSSI -86, (below minimum threshold)
- * test3 is @ 5Ghz with RSSI -50, however it has no associated saved config
- * test4 is @ 2Ghz with RSSI -62, no associated config, but is Ephemeral
- *
- * Expected behavior: test1 is chosen due to 5GHz signal is too weak (5GHz bonus can not
- * compensate).
- * test1 & test4's scanDetails are returned by 'getFilteredScanDetail()'
- */
- @Test
- public void testGetFilteredScanDetailsReturnsOnlyConsideredScanDetails_untrustedAllowed() {
- String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55",
- "c0:ff:ee:ee:e3:ee"};
- int[] frequencies = {2437, 5180, 5180, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]",
- "[WPA2-EAP-CCMP][ESS]"};
- int[] levels = {-60, -86, -50, -62};
- int[] security = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK, SECURITY_PSK};
- boolean[] meteredHints = {false, false, false, true};
- Integer[] scores = {null, null, null, 120};
-
- //Create all 4 scanDetails
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
-
- //Setup NetworkScoreCache for detecting ephemeral networks ("test4")
- configureScoreCache(scanDetails, scores, meteredHints);
- WifiConfiguration unTrustedNetworkCandidate = mock(WifiConfiguration.class);
- unTrustedNetworkCandidate.SSID = null;
- unTrustedNetworkCandidate.networkId = WifiConfiguration.INVALID_NETWORK_ID;
- ScanResult untrustedScanResult = scanDetails.get(3).getScanResult();
- when(mWifiConfigManager
- .wifiConfigurationFromScanResult(untrustedScanResult))
- .thenReturn(unTrustedNetworkCandidate);
- WifiConfiguration.NetworkSelectionStatus selectionStatus =
- mock(WifiConfiguration.NetworkSelectionStatus.class);
- when(unTrustedNetworkCandidate.getNetworkSelectionStatus()).thenReturn(selectionStatus);
-
- //Set up associated configs for test1 & test2
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(
- Arrays.copyOfRange(ssids, 0, 2), Arrays.copyOfRange(security, 0, 2));
- prepareConfigStore(savedConfigs);
- List<ScanDetail> savedScanDetails = new ArrayList<ScanDetail>(scanDetails.subList(0, 2));
- final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
- scanResultLinkConfiguration(savedConfigs, savedScanDetails);
-
- //Force mock ConfigManager to return null (and not an empty list) for "test3" & "test4"
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(eq(scanDetails.get(2)),
- anyBoolean())).thenReturn(null);
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(eq(scanDetails.get(3)),
- anyBoolean())).thenReturn(null);
-
- ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
- false /* forceSelectNetwork */,
- true /* isUntrustedConnectionsAllowed */,
- scanDetails,
- false, /* isLinkDebouncing */
- false, /* isConnected */
- true, /* isDisconnected */
- false /* isSupplicantTransient */);
-
- verifySelectedResult(chosenScanResult, candidate);
- //Verify two scanDetails returned in the filteredScanDetails
- assertEquals(2, mWifiQualifiedNetworkSelector.getFilteredScanDetails().size());
- assertEquals(mWifiQualifiedNetworkSelector.getFilteredScanDetails().get(0).first.toString(),
- scanDetails.get(0).toString());
- assertEquals(mWifiQualifiedNetworkSelector.getFilteredScanDetails().get(1).first.toString(),
- scanDetails.get(3).toString());
- }
-
-
- /**
- * Case #35 Test Filtering of potential candidate scanDetails (Untrusted disallowed)
- *
- * In this test. we simulate following scenario
- * WifiStateMachine is under disconnected state
- * test1 is @ 2GHz with RSSI -60
- * test2 is @ 5Ghz with RSSI -86, (below minimum threshold)
- * test3 is @ 5Ghz with RSSI -50, however it has no associated saved config
- * test4 is @ 2Ghz with RSSI -62, no associated config, but is Ephemeral
- *
- * Expected behavior: test1 is chosen due to 5GHz signal is too weak (5GHz bonus can not
- * compensate).
- * test1 & test4's scanDetails are returned by 'getFilteredScanDetail()'
- */
- @Test
- public void testGetFilteredScanDetailsReturnsOnlyConsideredScanDetails_untrustedDisallowed() {
- String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55",
- "c0:ff:ee:ee:e3:ee"};
- int[] frequencies = {2437, 5180, 5180, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]",
- "[WPA2-EAP-CCMP][ESS]"};
- int[] levels = {-60, -86, -50, -62};
- int[] security = {SECURITY_PSK, SECURITY_PSK, SECURITY_PSK, SECURITY_PSK};
- boolean[] meteredHints = {false, false, false, true};
- Integer[] scores = {null, null, null, 120};
-
- //Create all 4 scanDetails
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
-
- //Setup NetworkScoreCache for detecting ephemeral networks ("test4")
- configureScoreCache(scanDetails, scores, meteredHints);
- WifiConfiguration unTrustedNetworkCandidate = mock(WifiConfiguration.class);
- unTrustedNetworkCandidate.SSID = null;
- unTrustedNetworkCandidate.networkId = WifiConfiguration.INVALID_NETWORK_ID;
- ScanResult untrustedScanResult = scanDetails.get(3).getScanResult();
- when(mWifiConfigManager
- .wifiConfigurationFromScanResult(untrustedScanResult))
- .thenReturn(unTrustedNetworkCandidate);
- WifiConfiguration.NetworkSelectionStatus selectionStatus =
- mock(WifiConfiguration.NetworkSelectionStatus.class);
- when(unTrustedNetworkCandidate.getNetworkSelectionStatus()).thenReturn(selectionStatus);
-
- //Set up associated configs for test1 & test2
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(
- Arrays.copyOfRange(ssids, 0, 2), Arrays.copyOfRange(security, 0, 2));
- prepareConfigStore(savedConfigs);
- List<ScanDetail> savedScanDetails = new ArrayList<ScanDetail>(scanDetails.subList(0, 2));
- final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
- scanResultLinkConfiguration(savedConfigs, savedScanDetails);
-
- //Force mock ConfigManager to return null (and not an empty list) for "test3" & "test4"
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(eq(scanDetails.get(2)),
- anyBoolean())).thenReturn(null);
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(eq(scanDetails.get(3)),
- anyBoolean())).thenReturn(null);
-
- ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
- false /* forceSelectNetwork */,
- false /* isUntrustedConnectionsAllowed */,
- scanDetails,
- false, /* isLinkDebouncing */
- false, /* isConnected */
- true, /* isDisconnected */
- false /* isSupplicantTransient */);
-
- verifySelectedResult(chosenScanResult, candidate);
- //Verify two scanDetails returned in the filteredScanDetails
- assertEquals(1, mWifiQualifiedNetworkSelector.getFilteredScanDetails().size());
- assertEquals(mWifiQualifiedNetworkSelector.getFilteredScanDetails().get(0).first.toString(),
- scanDetails.get(0).toString());
- }
-
- /**
- * Case #36 Ignore an ephemeral network if it was previously deleted.
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is not connected to any network.
- * selectQualifiedNetwork() is called with 2 scan results, test1 and test2.
- * test1 is an open network with a low score. Additionally it's a metered network.
- * test2 is an open network with a good score but was previously deleted.
- * isUntrustedConnectionsAllowed is set to true.
- *
- * expected result: return test1 with meteredHint set to True.
- */
- @Test
- public void selectQualifiedNetworkDoesNotChooseDeletedEphemeral() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {5200, 5200};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
- int[] levels = {-70, -70};
- Integer[] scores = {20, 120};
- boolean[] meteredHints = {true, false};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- configureScoreCache(scanDetails, scores, meteredHints);
-
- // No saved networks.
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(any(ScanDetail.class),
- anyBoolean())).thenReturn(null);
-
- WifiConfiguration unTrustedNetworkCandidate = mock(WifiConfiguration.class);
- // Setup the config as an invalid candidate. This is done to workaround a Mockito issue.
- // Basically Mockito is unable to mock package-private methods in classes loaded from a
- // different Jar (like all of the framework code) which results in the actual saveNetwork()
- // method being invoked in this case. Because the config is invalid it quickly returns.
- unTrustedNetworkCandidate.SSID = null;
- unTrustedNetworkCandidate.networkId = WifiConfiguration.INVALID_NETWORK_ID;
- ScanResult untrustedScanResult = scanDetails.get(0).getScanResult();
- when(mWifiConfigManager
- .wifiConfigurationFromScanResult(untrustedScanResult))
- .thenReturn(unTrustedNetworkCandidate);
-
- // The second scan result is for an ephemeral network which was previously deleted
- when(mWifiConfigManager
- .wasEphemeralNetworkDeleted(ScanDetailUtil.createQuotedSSID(
- scanDetails.get(0).getScanResult().SSID)))
- .thenReturn(false);
- when(mWifiConfigManager
- .wasEphemeralNetworkDeleted(ScanDetailUtil.createQuotedSSID(
- scanDetails.get(1).getScanResult().SSID)))
- .thenReturn(true);
-
- WifiConfiguration.NetworkSelectionStatus selectionStatus =
- mock(WifiConfiguration.NetworkSelectionStatus.class);
- when(unTrustedNetworkCandidate.getNetworkSelectionStatus()).thenReturn(selectionStatus);
- when(selectionStatus.getCandidate()).thenReturn(untrustedScanResult);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
- false /* forceSelectNetwork */,
- true /* isUntrustedConnectionsAllowed */,
- scanDetails,
- false, /* isLinkDebouncing */
- false, /* isConnected */
- true, /* isDisconnected */
- false /* isSupplicantTransient */);
- verify(selectionStatus).setCandidate(untrustedScanResult);
- assertSame(candidate, unTrustedNetworkCandidate);
- assertEquals(meteredHints[0], candidate.meteredHint);
- }
-
- /**
- * Case #37 Choose the saved config that doesn't qualify for external scoring.
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is not connected to any network.
- * selectQualifiedNetwork() is called with 2 scan results, test1 and test2.
- * test1 is a saved network.
- * test2 is a saved network with useExternalScores set to true and a very high score.
- *
- * expected result: return test1 because saved networks that don't request external scoring
- * have a higher priority.
- */
- @Test
- public void selectQualifiedNetworkPrefersSavedWithoutExternalScores() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {5200, 5200};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
- int[] security = {SECURITY_PSK, SECURITY_PSK};
- int[] levels = {-70, -70};
- Integer[] scores = {null, 120};
- boolean[] meteredHints = {false, true};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- configureScoreCache(scanDetails, scores, meteredHints);
-
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(DEFAULT_SSIDS, security);
- savedConfigs[1].useExternalScores = true; // test2 is set to use external scores.
- prepareConfigStore(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(savedConfigs));
- scanResultLinkConfiguration(savedConfigs, scanDetails);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
- false /* forceSelectNetwork */,
- false /* isUntrustedConnectionsAllowed */,
- scanDetails,
- false, /* isLinkDebouncing */
- false, /* isConnected */
- true, /* isDisconnected */
- false /* isSupplicantTransient */);
- verifySelectedResult(scanDetails.get(0).getScanResult(), candidate);
- assertSame(candidate, savedConfigs[0]);
- }
-
- /**
- * Case #38 Choose the saved config that does qualify for external scoring when other saved
- * networks are not available.
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is not connected to any network.
- * selectQualifiedNetwork() is called with 2 scan results, test1 and test2.
- * test1 is a saved network with useExternalScores set to true and a very high score.
- * test2 is a saved network but not in range (not included in the scan results).
- *
- * expected result: return test1 because there are no better saved networks within range.
- */
- @Test
- public void selectQualifiedNetworkSelectsSavedWithExternalScores() {
- String[] ssids = {"\"test1\""};
- String[] bssids = {"6c:f3:7f:ae:8c:f3"};
- int[] frequencies = {5200};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
- int[] security = {SECURITY_PSK, SECURITY_PSK};
- int[] levels = {-70};
- Integer[] scores = {120};
- boolean[] meteredHints = {false};
-
- // Scan details only contains 1 ssid, test1.
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- configureScoreCache(scanDetails, scores, meteredHints);
-
- // The saved config contains 2 ssids, test1 & test2.
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(DEFAULT_SSIDS, security);
- savedConfigs[0].useExternalScores = true; // test1 is set to use external scores.
- prepareConfigStore(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(savedConfigs));
- scanResultLinkConfiguration(savedConfigs, scanDetails);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
- false /* forceSelectNetwork */,
- false /* isUntrustedConnectionsAllowed */,
- scanDetails,
- false, /* isLinkDebouncing */
- false, /* isConnected */
- true, /* isDisconnected */
- false /* isSupplicantTransient */);
- verifySelectedResult(scanDetails.get(0).getScanResult(), candidate);
- assertSame(candidate, savedConfigs[0]);
- }
-
- /**
- * Case #39 Choose the saved config that does qualify for external scoring over the
- * untrusted network with the same score.
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is not connected to any network.
- * selectQualifiedNetwork() is called with 2 scan results, test1 and test2.
- * test1 is a saved network with useExternalScores set to true and the same score as test1.
- * test2 is NOT saved network but in range with a good external score.
- *
- * expected result: return test1 because the tie goes to the saved network.
- */
- @Test
- public void selectQualifiedNetworkPrefersSavedWithExternalScoresOverUntrusted() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {5200, 5200};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
- int[] security = {SECURITY_PSK, SECURITY_PSK};
- int[] levels = {-70, -70};
- Integer[] scores = {120, 120};
- boolean[] meteredHints = {false, true};
-
- // Both networks are in the scan results.
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- configureScoreCache(scanDetails, scores, meteredHints);
-
- // Set up the associated configs only for test1
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(
- Arrays.copyOfRange(ssids, 0, 1), Arrays.copyOfRange(security, 0, 1));
- savedConfigs[0].useExternalScores = true; // test1 is set to use external scores.
- prepareConfigStore(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(savedConfigs));
- scanResultLinkConfiguration(savedConfigs, scanDetails);
- WifiConfiguration unTrustedNetworkCandidate = mock(WifiConfiguration.class);
- when(mWifiConfigManager
- .wifiConfigurationFromScanResult(scanDetails.get(1).getScanResult()))
- .thenReturn(unTrustedNetworkCandidate);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
- false /* forceSelectNetwork */,
- true /* isUntrustedConnectionsAllowed */,
- scanDetails,
- false, /* isLinkDebouncing */
- false, /* isConnected */
- true, /* isDisconnected */
- false /* isSupplicantTransient */);
- verifySelectedResult(scanDetails.get(0).getScanResult(), candidate);
- assertSame(candidate, savedConfigs[0]);
- }
-
- /**
- * Case #40 Choose the ephemeral config over the saved config that does qualify for external
- * scoring because the untrusted network has a higher score.
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is not connected to any network.
- * selectQualifiedNetwork() is called with 2 scan results, test1 and test2.
- * test1 is a saved network with useExternalScores set to true and a low score.
- * test2 is NOT saved network but in range with a good external score.
- *
- * expected result: return test2 because it has a better score.
- */
- @Test
- public void selectQualifiedNetworkPrefersUntrustedOverScoredSaved() {
- String[] ssids = DEFAULT_SSIDS;
- String[] bssids = DEFAULT_BSSIDS;
- int[] frequencies = {5200, 5200};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[ESS]"};
- int[] security = {SECURITY_PSK, SECURITY_PSK};
- int[] levels = {-70, -70};
- Integer[] scores = {10, 120};
- boolean[] meteredHints = {false, true};
-
- // Both networks are in the scan results.
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- configureScoreCache(scanDetails, scores, meteredHints);
-
- // Set up the associated configs only for test1
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(
- Arrays.copyOfRange(ssids, 0, 1), Arrays.copyOfRange(security, 0, 1));
- savedConfigs[0].useExternalScores = true; // test1 is set to use external scores.
- prepareConfigStore(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(savedConfigs));
- scanResultLinkConfiguration(savedConfigs, scanDetails);
- WifiConfiguration unTrustedNetworkCandidate = mock(WifiConfiguration.class);
- unTrustedNetworkCandidate.SSID = null;
- unTrustedNetworkCandidate.networkId = WifiConfiguration.INVALID_NETWORK_ID;
- ScanResult untrustedScanResult = scanDetails.get(1).getScanResult();
- when(mWifiConfigManager
- .wifiConfigurationFromScanResult(untrustedScanResult))
- .thenReturn(unTrustedNetworkCandidate);
- WifiConfiguration.NetworkSelectionStatus selectionStatus =
- mock(WifiConfiguration.NetworkSelectionStatus.class);
- when(unTrustedNetworkCandidate.getNetworkSelectionStatus()).thenReturn(selectionStatus);
- when(selectionStatus.getCandidate()).thenReturn(untrustedScanResult);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(
- false /* forceSelectNetwork */,
- true /* isUntrustedConnectionsAllowed */,
- scanDetails,
- false, /* isLinkDebouncing */
- false, /* isConnected */
- true, /* isDisconnected */
- false /* isSupplicantTransient */);
- verify(selectionStatus).setCandidate(untrustedScanResult);
- assertSame(unTrustedNetworkCandidate, candidate);
- }
-
- /**
- * Case #41 Ensure the ExternalScoreEvaluator correctly selects the untrusted network.
- *
- * In this test. we simulate following scenario:
- * The ExternalScoreEvaluator is asked to evaluate 1 untrusted network and 1 saved network.
- * The untrusted network has the higher score.
- *
- * expected result: The untrusted network is determined to be the best network.
- */
- @Test
- public void externalScoreEvaluator_untrustedIsBest() {
- WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator =
- new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true);
- ScanResult untrustedScanResult = new ScanResult();
- int untrustedScore = 100;
- evaluator.evalUntrustedCandidate(untrustedScore, untrustedScanResult);
-
- ScanResult savedScanResult = new ScanResult();
- int savedScore = 50;
- WifiConfiguration savedConfig = new WifiConfiguration();
- evaluator.evalSavedCandidate(savedScore, savedConfig, savedScanResult);
- assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator
- .BestCandidateType.UNTRUSTED_NETWORK, evaluator.getBestCandidateType());
- assertEquals(untrustedScore, evaluator.getHighScore());
- assertSame(untrustedScanResult, evaluator.getScanResultCandidate());
- }
-
- /**
- * Case #42 Ensure the ExternalScoreEvaluator correctly selects the saved network.
- *
- * In this test. we simulate following scenario:
- * The ExternalScoreEvaluator is asked to evaluate 1 untrusted network and 1 saved network.
- * The saved network has the higher score.
- *
- * expected result: The saved network is determined to be the best network.
- */
- @Test
- public void externalScoreEvaluator_savedIsBest() {
- WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator =
- new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true);
- ScanResult untrustedScanResult = new ScanResult();
- int untrustedScore = 50;
- evaluator.evalUntrustedCandidate(untrustedScore, untrustedScanResult);
-
- ScanResult savedScanResult = new ScanResult();
- int savedScore = 100;
- WifiConfiguration savedConfig = new WifiConfiguration();
- evaluator.evalSavedCandidate(savedScore, savedConfig, savedScanResult);
- assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator
- .BestCandidateType.SAVED_NETWORK, evaluator.getBestCandidateType());
- assertEquals(savedScore, evaluator.getHighScore());
- assertSame(savedScanResult, evaluator.getScanResultCandidate());
- }
-
- /**
- * Case #43 Ensure the ExternalScoreEvaluator correctly selects the saved network if a
- * tie occurs.
- *
- * In this test. we simulate following scenario:
- * The ExternalScoreEvaluator is asked to evaluate 1 untrusted network and 1 saved network.
- * Both networks have the same score.
- *
- * expected result: The saved network is determined to be the best network.
- */
- @Test
- public void externalScoreEvaluator_tieScores() {
- WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator =
- new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true);
- ScanResult untrustedScanResult = new ScanResult();
- int untrustedScore = 100;
- evaluator.evalUntrustedCandidate(untrustedScore, untrustedScanResult);
-
- ScanResult savedScanResult = new ScanResult();
- int savedScore = 100;
- WifiConfiguration savedConfig = new WifiConfiguration();
- evaluator.evalSavedCandidate(savedScore, savedConfig, savedScanResult);
- assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator
- .BestCandidateType.SAVED_NETWORK, evaluator.getBestCandidateType());
- assertEquals(savedScore, evaluator.getHighScore());
- assertSame(savedScanResult, evaluator.getScanResultCandidate());
- }
-
- /**
- * Case #44 Ensure the ExternalScoreEvaluator correctly selects the saved network out of
- * multiple options.
- *
- * In this test. we simulate following scenario:
- * The ExternalScoreEvaluator is asked to evaluate 2 untrusted networks and 2 saved networks.
- * The high scores are equal and the low scores differ.
- *
- * expected result: The saved network is determined to be the best network.
- */
- @Test
- public void externalScoreEvaluator_multipleScores() {
- WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator =
- new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true);
- ScanResult untrustedScanResult = new ScanResult();
- int untrustedScore = 100;
- evaluator.evalUntrustedCandidate(untrustedScore, untrustedScanResult);
- evaluator.evalUntrustedCandidate(80, new ScanResult());
-
- ScanResult savedScanResult = new ScanResult();
- int savedScore = 100;
- WifiConfiguration savedConfig = new WifiConfiguration();
- evaluator.evalSavedCandidate(savedScore, savedConfig, savedScanResult);
- evaluator.evalSavedCandidate(90, new WifiConfiguration(), new ScanResult());
- assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator
- .BestCandidateType.SAVED_NETWORK, evaluator.getBestCandidateType());
- assertEquals(savedScore, evaluator.getHighScore());
- assertSame(savedScanResult, evaluator.getScanResultCandidate());
- }
-
- /**
- * Case #45 Ensure the ExternalScoreEvaluator correctly handles NULL score inputs.
- *
- * In this test we simulate following scenario:
- * The ExternalScoreEvaluator is asked to evaluate both types of candidates with NULL scores.
- *
- * expected result: No crashes. The best candidate type is returned as NONE.
- */
- @Test
- public void externalScoreEvaluator_nullScores() {
- WifiQualifiedNetworkSelector.ExternalScoreEvaluator evaluator =
- new WifiQualifiedNetworkSelector.ExternalScoreEvaluator(mLocalLog, true);
- evaluator.evalUntrustedCandidate(null, new ScanResult());
- assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator
- .BestCandidateType.NONE, evaluator.getBestCandidateType());
- evaluator.evalSavedCandidate(null, new WifiConfiguration(), new ScanResult());
- assertEquals(WifiQualifiedNetworkSelector.ExternalScoreEvaluator
- .BestCandidateType.NONE, evaluator.getBestCandidateType());
- }
-
- /**
- * Case #46 Choose 2.4GHz BSSID with stronger RSSI value over
- * 5GHz BSSID with weaker RSSI value
- *
- * In this test. we simulate following scenario:
- * Two APs are found in scan results
- * BSSID1 is @ 5GHz with RSSI -82
- * BSSID2 is @ 2Ghz with RSSI -72
- * These two BSSIDs get exactly the same QNS score
- *
- * expect BSSID2 to be chosen as it has stronger RSSI value
- */
- @Test
- public void chooseStrongerRssiOver5GHz() {
- String[] ssids = {"\"test1\"", "\"test1\""};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
- int[] frequencies = {5220, 2437};
- String[] caps = {"[ESS]", "[ESS]"};
- int[] levels = {-82, -72};
- int[] security = {SECURITY_NONE, SECURITY_NONE};
-
- 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();
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
-
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #47 Choose the currently connected BSSID after a firmware initiated roaming.
- *
- * In this test. we simulate following scenario:
- * Two APs are found in scan results
- * BSSID1 is @ 2.4GHz with RSSI -78
- * BSSID2 is @ 2.4Ghz with RSSI -77
- * BSSID2 is chosen because of stronger RSSI. Then firmware initiates
- * a roaming to BSSID1. QNS now selects BSSID1 because of the bonus for currently
- * connected network even if BSSID 2 has slightly stronger signal strengh.
- *
- * expect BSSID2 to be chosen after firmware roaming
- */
- @Test
- public void chooseCurrentlyConnectedBssid() {
- String[] ssids = {"\"test1\"", "\"test1\""};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
- int[] frequencies = {2437, 2437};
- String[] caps = {"[ESS]", "[ESS]"};
- int[] levels = {-78, -77};
- int[] security = {SECURITY_NONE, SECURITY_NONE};
-
- 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);
-
- // Choose BSSID2 as it has stronger RSSI
- ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
- verifySelectedResult(chosenScanResult, candidate);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[1]);
- when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
-
- // Choose BSSID2 as it has stronger RSSI and it is the currently connected BSSID
- chosenScanResult = scanDetails.get(1).getScanResult();
- candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(true,
- false, scanDetails, false, true, false, false);
- verifySelectedResult(chosenScanResult, candidate);
-
- // Pretend firmware roamed the device to BSSID1
- when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
-
- // Choose BSSID1 as it is the currently connected BSSID even if BSSID2 has slightly
- // higher RSSI value.
- chosenScanResult = scanDetails.get(0).getScanResult();
- candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(true,
- false, scanDetails, false, true, false, false);
- verifySelectedResult(chosenScanResult, candidate);
- }
-
- /**
- * Case #48 no new QNS if current network doesn't show up in the
- * scan results.
- *
- * In this test. we simulate following scenario:
- * WifiStateMachine is under connected state and 2.4GHz test1 is connected.
- * The second scan results contains test2 which is 5GHz but no test1. Skip
- * QNS to avoid aggressive network switching.
- *
- * expected return null
- */
- @Test
- public void noNewQNSCurrentNetworkNotInScanResults() {
- //Prepare saved network configurations.
- String[] ssidsConfig = DEFAULT_SSIDS;
- int[] security = {SECURITY_PSK, SECURITY_PSK};
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssidsConfig, security);
- prepareConfigStore(savedConfigs);
- final List<WifiConfiguration> savedNetwork = Arrays.asList(savedConfigs);
- when(mWifiConfigManager.getSavedNetworks()).thenReturn(savedNetwork);
-
- //Prepare the first scan results.
- String[] ssids = {DEFAULT_SSIDS[0]};
- String[] bssids = {DEFAULT_BSSIDS[0]};
- int[] frequencies = {2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
- int[] levels = {-78};
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
-
- //Connect to test1.
- mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false, false, scanDetails, false,
- false, true, false);
-
- when(mWifiInfo.getNetworkId()).thenReturn(1);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
- when(mWifiInfo.is24GHz()).thenReturn(true);
- mWifiQualifiedNetworkSelector.setWifiNetworkScoreCache(null);
- when(mWifiConfigManager.getEnableAutoJoinWhenAssociated()).thenReturn(true);
- when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime() + 11 * 1000);
-
- //Prepare the second scan results which doesn't contain test1.
- ssids[0] = DEFAULT_SSIDS[1];
- bssids[0] = DEFAULT_BSSIDS[1];
- frequencies[0] = 5180;
- caps[0] = "[WPA2-EAP-CCMP][ESS]";
- levels[0] = WifiQualifiedNetworkSelector.QUALIFIED_RSSI_5G_BAND;
- scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- scanResultLinkConfiguration(savedConfigs, scanDetails);
-
- //Skip the second network selection since current connected network is
- //missing from the scan results.
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
- assertEquals("Expect no network selection", null, candidate);
- }
-
- boolean compareCarrierConfigs(WifiConfiguration candidate, WifiConfiguration carrierConfig) {
- if (!candidate.SSID.equals(carrierConfig.SSID)) {
- return false;
- }
- if (!candidate.ephemeral || carrierConfig.ephemeral) {
- return false;
- }
- if (!candidate.isCarrierNetwork || carrierConfig.isCarrierNetwork) {
- return false;
- }
- if (candidate.enterpriseConfig.getEapMethod() !=
- carrierConfig.enterpriseConfig.getEapMethod()) {
- return false;
- }
- return true;
- }
-
- /**
- * Case #49 Between two 2G Carrier networks, choose the one with stronger RSSI value
- * if other conditions are the same and the RSSI values are not staturated.
- */
- @Test
- public void chooseStrongerRssi2GCarrierNetwork() {
-
- String[] ssids = {"TEST1", "TEST2"};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
- int[] frequencies = {2470, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
- int[] levels = {-65,-55};
- int[] security = {SECURITY_PSK, SECURITY_PSK};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(any(ScanDetail.class),
- any(Boolean.class))).thenReturn(null);
- when(mWifiConfigManager.saveNetworkAndSetCandidate(any(WifiConfiguration.class),
- any(ScanResult.class))).then(AdditionalAnswers.returnsFirstArg());
- when(mWifiConfigManager.getScanResultCandidate(any(WifiConfiguration.class)))
- .thenReturn(chosenScanResult);
- when(mWifiConfigManager.getIsCarrierNetworkEnabledByUser()).thenReturn(true);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
- assertTrue(compareCarrierConfigs(candidate, mCarrierConfiguredNetworks.get(1)));
- }
-
-
- /**
- * Case #50 Choose 5G over 2G.
- */
- @Test
- public void choose5GNetworkOver2GNetwork() {
-
- String[] ssids = {"TEST1", "TEST2"};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
- int[] frequencies = {2437, 5240};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
- int[] levels = {-65,-55};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(any(ScanDetail.class),
- any(Boolean.class))).thenReturn(null);
- when(mWifiConfigManager.saveNetworkAndSetCandidate(any(WifiConfiguration.class),
- any(ScanResult.class))).then(AdditionalAnswers.returnsFirstArg());
- when(mWifiConfigManager.getScanResultCandidate(any(WifiConfiguration.class)))
- .thenReturn(chosenScanResult);
- when(mWifiConfigManager.getIsCarrierNetworkEnabledByUser()).thenReturn(true);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
- assertTrue(compareCarrierConfigs(candidate, mCarrierConfiguredNetworks.get(1)));
- }
-
- /**
- * Case #51 Stay on same BSSID & SSID.
- */
- @Test
- public void chooseSameNetwork() {
-
- String[] ssids = {"TEST1", "TEST2"};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
- int[] frequencies = {2470, 2437};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
- int[] levels = {-65,-55};
- int[] security = {SECURITY_PSK, SECURITY_PSK};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- WifiConfiguration[] savedConfigs = generateWifiConfigurations(ssids, security);
- prepareConfigStore(savedConfigs);
-
- ScanResult chosenScanResult = scanDetails.get(0).getScanResult();
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(any(ScanDetail.class),
- any(Boolean.class))).thenReturn(null);
- when(mWifiConfigManager.saveNetworkAndSetCandidate(any(WifiConfiguration.class),
- any(ScanResult.class))).then(AdditionalAnswers.returnsFirstArg());
- when(mWifiConfigManager.getScanResultCandidate(any(WifiConfiguration.class)))
- .thenReturn(chosenScanResult);
- when(mWifiInfo.getNetworkId()).thenReturn(0);
- when(mWifiInfo.getBSSID()).thenReturn(bssids[0]);
- when(mWifiConfigManager.getIsCarrierNetworkEnabledByUser()).thenReturn(true);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, true, false, false);
-
- assertTrue(compareCarrierConfigs(candidate, mCarrierConfiguredNetworks.get(0)));
- }
-
- /**
- * Case #52 Test condition where no Carrier networks are defined.
- */
- @Test
- public void testNoCarrierNetworks() {
-
- String[] ssids = {"TEST1", "TEST2"};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
- int[] frequencies = {5200, 5240};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
- // test2 has slightly stronger RSSI value than test1
- int[] levels = {-65,-53};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
-
- List<WifiConfiguration> nullCarrierConfiguredNetworks = new ArrayList<WifiConfiguration>();
- mWifiQualifiedNetworkSelector.setCarrierConfiguredNetworks(nullCarrierConfiguredNetworks);
- when(mWifiConfigManager.getIsCarrierNetworkEnabledByUser()).thenReturn(true);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- true, scanDetails, false, false, true, false);
- assertEquals("Expect no network selection", null, candidate);
- }
-
- /**
- * Case #53 Test condition where no Carrier networks are defined.
- */
- @Test
- public void testParseCarrierInfoSuccess() {
- String[] wifiArray = new String[3];
- wifiArray[0] = "V2lmaSBFeHRyYQ==|2|4";
- wifiArray[1] = "R29vZ2xlLUE=|2|4";
- wifiArray[2] = "R29vZ2xlLUd1ZXN0|2|4";
-
- List<WifiConfiguration> configList =
- mWifiQualifiedNetworkSelector.parseCarrierSuppliedWifiInfo(wifiArray);
- assertEquals("Expect right number of etnries", configList.size(), 3);
- assertEquals("Expect right network", configList.get(0).SSID, "\"Wifi Extra\"");
- assertEquals("Expect right network", configList.get(1).SSID, "\"Google-A\"");
- assertEquals("Expect right network", configList.get(2).SSID, "\"Google-Guest\"");
- assertTrue("Expect right key",
- configList.get(0).allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP));
- assertEquals("Expect right EAP method",
- configList.get(0).enterpriseConfig.getEapMethod(), 4);
- }
-
- /**
- * Case #54 Test condition where string has non-numerics.
- */
- @Test
- public void testParseCarrierInfoBadEntries1() {
- String[] wifiArray = new String[3];
- wifiArray[0] = "V2lmaSBFeHRyYQ==|2|4";
- wifiArray[1] = "R29vZ2xlLUE=|2|A"; //Invalid entry. Non-numeric.
- wifiArray[2] = "R29vZ2xlLUd1ZXN0|2|4";
-
- List<WifiConfiguration> configList =
- mWifiQualifiedNetworkSelector.parseCarrierSuppliedWifiInfo(wifiArray);
- assertEquals("Expect right number of etnries", configList.size(), 2);
- assertEquals("Expect right network", configList.get(0).SSID, "\"Wifi Extra\"");
- assertEquals("Expect right network", configList.get(1).SSID, "\"Google-Guest\"");
- }
-
- /**
- * Case #55 Test condition where the config does not have the right number of entries.
- */
- @Test
- public void testParseCarrierInfoBadEntries2() {
- String[] wifiArray = new String[3];
- wifiArray[0] = "V2lmaSBFeHRyYQ==|2"; //Invalid number of entries
- wifiArray[1] = "R29vZ2xlLUE=|2|4";
- wifiArray[2] = "R29vZ2xlLUd1ZXN0|2|4";
-
- List<WifiConfiguration> configList =
- mWifiQualifiedNetworkSelector.parseCarrierSuppliedWifiInfo(wifiArray);
- assertEquals("Expect right network", configList.get(0).SSID, "\"Google-A\"");
- assertEquals("Expect right network", configList.get(1).SSID, "\"Google-Guest\"");
- }
-
- /**
- * Case #56 Test invalid base-64.
- */
- @Test
- public void testParseCarrierInfoBadBase64() {
- String[] wifiArray = new String[3];
- wifiArray[0] = "V2lmaSBFeHRyYQ==|2|4";
- wifiArray[1] = "xyz==|2|4"; //Invalid base64
- wifiArray[2] = "R29vZ2xlLUd1ZXN0|2|4";
-
- List<WifiConfiguration> configList =
- mWifiQualifiedNetworkSelector.parseCarrierSuppliedWifiInfo(wifiArray);
- assertEquals("Expect right network", configList.get(0).SSID, "\"Wifi Extra\"");
- assertEquals("Expect right network", configList.get(1).SSID, "\"Google-Guest\"");
- }
-
- /**
- * Case #56 Test invalid eap-method
- */
- @Test
- public void testParseCarrierInfoBadEapMethod() {
- String[] wifiArray = new String[3];
- wifiArray[0] = "V2lmaSBFeHRyYQ==|2|4";
- wifiArray[1] = "R29vZ2xlLUE=|2|4";
- wifiArray[2] = "R29vZ2xlLUd1ZXN0|2|11"; //Invalid eap-method
-
- List<WifiConfiguration> configList =
- mWifiQualifiedNetworkSelector.parseCarrierSuppliedWifiInfo(wifiArray);
- assertEquals("Expect right network", configList.get(0).SSID, "\"Wifi Extra\"");
- assertEquals("Expect right network", configList.get(1).SSID, "\"Google-A\"");
- }
-
- /**
- * Case #56 Test invalid key
- */
- @Test
- public void testParseCarrierInfoBadKey() {
- String[] wifiArray = new String[3];
- wifiArray[0] = "V2lmaSBFeHRyYQ==|2|4";
- wifiArray[1] = "R29vZ2xlLUE=|9|4"; //Invalid key
- wifiArray[2] = "R29vZ2xlLUd1ZXN0|2|4";
-
- List<WifiConfiguration> configList =
- mWifiQualifiedNetworkSelector.parseCarrierSuppliedWifiInfo(wifiArray);
- assertEquals("Expect right network", configList.get(0).SSID, "\"Wifi Extra\"");
- assertEquals("Expect right network", configList.get(2).SSID, "\"Google-Guest\"");
- }
-
- /**
- * Case #57 Test condition where no Carrier networks are defined.
- */
- @Test
- public void testCarrierNotEnabledByUser() {
-
- String[] ssids = {"TEST1", "TEST2"};
- String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4"};
- int[] frequencies = {2437, 5240};
- String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
- int[] levels = {-65,-55};
-
- List<ScanDetail> scanDetails = getScanDetails(ssids, bssids, frequencies, caps, levels);
- ScanResult chosenScanResult = scanDetails.get(1).getScanResult();
- when(mWifiConfigManager.updateSavedNetworkWithNewScanDetail(any(ScanDetail.class),
- any(Boolean.class))).thenReturn(null);
- when(mWifiConfigManager.saveNetworkAndSetCandidate(any(WifiConfiguration.class),
- any(ScanResult.class))).then(AdditionalAnswers.returnsFirstArg());
- when(mWifiConfigManager.getScanResultCandidate(any(WifiConfiguration.class)))
- .thenReturn(chosenScanResult);
- when(mWifiConfigManager.getIsCarrierNetworkEnabledByUser()).thenReturn(false);
-
- WifiConfiguration candidate = mWifiQualifiedNetworkSelector.selectQualifiedNetwork(false,
- false, scanDetails, false, false, true, false);
- assertEquals("Expect no network selection", null, candidate);
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java b/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
new file mode 100644
index 0000000..41f14dd
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiScoreReportTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.NetworkAgent;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+
+import com.android.internal.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiScoreReport}.
+ */
+public class WifiScoreReportTest {
+
+ private static final int CELLULAR_THRESHOLD_SCORE = 50;
+
+ WifiConfiguration mWifiConfiguration;
+ WifiScoreReport mWifiScoreReport;
+ ScanDetailCache mScanDetailCache;
+ WifiInfo mWifiInfo;
+ @Mock Context mContext;
+ @Mock NetworkAgent mNetworkAgent;
+ @Mock Resources mResources;
+ @Mock WifiConfigManager mWifiConfigManager;
+ @Mock WifiMetrics mWifiMetrics;
+
+ /**
+ * Sets up resource values for testing
+ *
+ * See frameworks/base/core/res/res/values/config.xml
+ */
+ private void setUpResources(Resources resources) {
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz))
+ .thenReturn(-82);
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_5GHz))
+ .thenReturn(-70);
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_5GHz))
+ .thenReturn(-57);
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz))
+ .thenReturn(-85);
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_low_rssi_threshold_24GHz))
+ .thenReturn(-73);
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_rssi_threshold_24GHz))
+ .thenReturn(-60);
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_link_speed_24))
+ .thenReturn(6); // Mbps
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_bad_link_speed_5))
+ .thenReturn(12);
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_link_speed_24))
+ .thenReturn(24);
+ when(resources.getInteger(
+ R.integer.config_wifi_framework_wifi_score_good_link_speed_5))
+ .thenReturn(36);
+ }
+
+ /**
+ * Sets up for unit test
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ setUpResources(mResources);
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "nooooooooooo";
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ config.hiddenSSID = false;
+ mWifiInfo = new WifiInfo();
+ mWifiInfo.setFrequency(2412);
+ when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(config));
+ when(mWifiConfigManager.getConfiguredNetwork(anyInt())).thenReturn(config);
+ mWifiConfiguration = config;
+ int maxSize = 10;
+ int trimSize = 5;
+ mScanDetailCache = new ScanDetailCache(config, maxSize, trimSize);
+ // TODO: populate the cache, but probably in the test cases, not here.
+ when(mWifiConfigManager.getScanDetailCacheForNetwork(anyInt()))
+ .thenReturn(mScanDetailCache);
+ when(mContext.getResources()).thenReturn(mResources);
+ mWifiScoreReport = new WifiScoreReport(mContext, mWifiConfigManager);
+ }
+
+ /**
+ * Cleans up after test
+ */
+ @After
+ public void tearDown() throws Exception {
+ mResources = null;
+ mWifiScoreReport = null;
+ mWifiConfigManager = null;
+ mWifiMetrics = null;
+ }
+
+ /**
+ * Test for score reporting
+ *
+ * The score should be sent to both the NetworkAgent and the
+ * WifiMetrics
+ */
+ @Test
+ public void calculateAndReportScoreSucceeds() throws Exception {
+ int aggressiveHandover = 0;
+ mWifiInfo.setRssi(-77);
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo,
+ mNetworkAgent, aggressiveHandover, mWifiMetrics);
+ verify(mNetworkAgent).sendNetworkScore(anyInt());
+ verify(mWifiMetrics).incrementWifiScoreCount(anyInt());
+ }
+
+ /**
+ * Test for operation with null NetworkAgent
+ *
+ * Expect to not die, and to calculate the score and report to metrics.
+ */
+ @Test
+ public void networkAgentMayBeNull() throws Exception {
+ mWifiInfo.setRssi(-33);
+ mWifiScoreReport.enableVerboseLogging(true);
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, null, 0, mWifiMetrics);
+ verify(mWifiMetrics).incrementWifiScoreCount(anyInt());
+ }
+
+ /**
+ * Exercise the rates with low RSSI
+ *
+ * The setup has a low (not bad) RSSI, and data movement (txSuccessRate) above
+ * the threshold.
+ *
+ * Expect a score above threshold.
+ */
+ @Test
+ public void allowLowRssiIfDataIsMoving() throws Exception {
+ mWifiInfo.setRssi(-80);
+ mWifiInfo.setLinkSpeed(6); // Mbps
+ mWifiInfo.txSuccessRate = 5.1; // proportional to pps
+ mWifiInfo.rxSuccessRate = 5.1;
+ for (int i = 0; i < 10; i++) {
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, 0, mWifiMetrics);
+ }
+ int score = mWifiInfo.score;
+ assertTrue(score > CELLULAR_THRESHOLD_SCORE);
+ }
+
+ /**
+ * Bad RSSI without data moving should allow handoff
+ *
+ * The setup has a bad RSSI, and the txSuccessRate is below threshold; several
+ * scoring iterations are performed.
+ *
+ * Expect the score to drop below the handoff threshold.
+ */
+ @Test
+ public void giveUpOnBadRssiWhenDataIsNotMoving() throws Exception {
+ mWifiInfo.setRssi(-100);
+ mWifiInfo.setLinkSpeed(6); // Mbps
+ mWifiInfo.setFrequency(5220);
+ mWifiScoreReport.enableVerboseLogging(true);
+ mWifiInfo.txSuccessRate = 0.1;
+ mWifiInfo.rxSuccessRate = 0.1;
+ for (int i = 0; i < 10; i++) {
+ mWifiScoreReport.calculateAndReportScore(mWifiInfo, mNetworkAgent, 0, mWifiMetrics);
+ }
+ int score = mWifiInfo.score;
+ assertTrue(score < CELLULAR_THRESHOLD_SCORE);
+ verify(mNetworkAgent, atLeast(1)).sendNetworkScore(score);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
new file mode 100644
index 0000000..d5bfb20
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java
@@ -0,0 +1,1583 @@
+/*
+ * 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 android.net.wifi.WifiManager.HOTSPOT_FAILED;
+import static android.net.wifi.WifiManager.HOTSPOT_STARTED;
+import static android.net.wifi.WifiManager.HOTSPOT_STOPPED;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
+import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
+import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
+import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
+import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED;
+import static android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
+import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
+
+import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR;
+import static com.android.server.wifi.WifiController.CMD_SET_AP;
+import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.IpConfiguration;
+import android.net.wifi.ScanSettings;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.LocalOnlyHotspotCallback;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.IPowerManager;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.os.WorkSource;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.server.wifi.WifiServiceImpl.LocalOnlyRequestorCallback;
+import com.android.server.wifi.util.WifiAsyncChannel;
+import com.android.server.wifi.util.WifiPermissionsUtil;
+
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
+
+/**
+ * Unit tests for {@link WifiServiceImpl}.
+ *
+ * Note: this is intended to build up over time and will not immediately cover the entire file.
+ */
+@SmallTest
+public class WifiServiceImplTest {
+
+ private static final String TAG = "WifiServiceImplTest";
+ private static final String SCAN_PACKAGE_NAME = "scanPackage";
+ private static final String WHITE_LIST_SCAN_PACKAGE_NAME = "whiteListScanPackage";
+ private static final int DEFAULT_VERBOSE_LOGGING = 0;
+ private static final long WIFI_BACKGROUND_SCAN_INTERVAL = 10000;
+ private static final String ANDROID_SYSTEM_PACKAGE = "android";
+ private static final String TEST_PACKAGE_NAME = "TestPackage";
+ private static final String SYSUI_PACKAGE_NAME = "com.android.systemui";
+ private static final int TEST_PID = 6789;
+ private static final int TEST_PID2 = 9876;
+ private static final String WIFI_IFACE_NAME = "wlan0";
+
+ private WifiServiceImpl mWifiServiceImpl;
+ private TestLooper mLooper;
+ private PowerManager mPowerManager;
+ private Handler mHandler;
+ private Messenger mAppMessenger;
+ private int mPid;
+ private int mPid2 = Process.myPid();
+
+ final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ final ArgumentCaptor<IntentFilter> mIntentFilterCaptor =
+ ArgumentCaptor.forClass(IntentFilter.class);
+
+ final ArgumentCaptor<Message> mMessageCaptor = ArgumentCaptor.forClass(Message.class);
+ final ArgumentCaptor<SoftApModeConfiguration> mSoftApModeConfigCaptor =
+ ArgumentCaptor.forClass(SoftApModeConfiguration.class);
+
+ @Mock Context mContext;
+ @Mock WifiInjector mWifiInjector;
+ @Mock Clock mClock;
+ @Mock WifiController mWifiController;
+ @Mock WifiTrafficPoller mWifiTrafficPoller;
+ @Mock WifiStateMachine mWifiStateMachine;
+ @Mock HandlerThread mHandlerThread;
+ @Mock AsyncChannel mAsyncChannel;
+ @Mock Resources mResources;
+ @Mock FrameworkFacade mFrameworkFacade;
+ @Mock WifiLockManager mLockManager;
+ @Mock WifiMulticastLockManager mWifiMulticastLockManager;
+ @Mock WifiLastResortWatchdog mWifiLastResortWatchdog;
+ @Mock WifiBackupRestore mWifiBackupRestore;
+ @Mock WifiMetrics mWifiMetrics;
+ @Mock WifiPermissionsUtil mWifiPermissionsUtil;
+ @Mock WifiSettingsStore mSettingsStore;
+ @Mock ContentResolver mContentResolver;
+ @Mock UserManager mUserManager;
+ @Mock WifiConfiguration mApConfig;
+ @Mock ActivityManager mActivityManager;
+ @Mock AppOpsManager mAppOpsManager;
+ @Mock IBinder mAppBinder;
+ @Mock WifiNotificationController mWifiNotificationController;
+ @Mock LocalOnlyHotspotRequestInfo mRequestInfo;
+ @Mock LocalOnlyHotspotRequestInfo mRequestInfo2;
+
+ @Spy FakeWifiLog mLog;
+
+ private class WifiAsyncChannelTester {
+ private static final String TAG = "WifiAsyncChannelTester";
+ public static final int CHANNEL_STATE_FAILURE = -1;
+ public static final int CHANNEL_STATE_DISCONNECTED = 0;
+ public static final int CHANNEL_STATE_HALF_CONNECTED = 1;
+ public static final int CHANNEL_STATE_FULLY_CONNECTED = 2;
+
+ private int mState = CHANNEL_STATE_DISCONNECTED;
+ private WifiAsyncChannel mChannel;
+ private WifiLog mAsyncTestLog;
+
+ WifiAsyncChannelTester(WifiInjector wifiInjector) {
+ mAsyncTestLog = wifiInjector.makeLog(TAG);
+ }
+
+ public int getChannelState() {
+ return mState;
+ }
+
+ public void connect(final Looper looper, final Messenger messenger,
+ final Handler incomingMessageHandler) {
+ assertEquals("AsyncChannel must be in disconnected state",
+ CHANNEL_STATE_DISCONNECTED, mState);
+ mChannel = new WifiAsyncChannel(TAG);
+ mChannel.setWifiLog(mLog);
+ Handler handler = new Handler(mLooper.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ mChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ mState = CHANNEL_STATE_HALF_CONNECTED;
+ } else {
+ mState = CHANNEL_STATE_FAILURE;
+ }
+ break;
+ case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+ mState = CHANNEL_STATE_FULLY_CONNECTED;
+ break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ mState = CHANNEL_STATE_DISCONNECTED;
+ break;
+ default:
+ incomingMessageHandler.handleMessage(msg);
+ break;
+ }
+ }
+ };
+ mChannel.connect(null, handler, messenger);
+ }
+ }
+
+ @Before public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mLooper = new TestLooper();
+ mHandler = spy(new Handler(mLooper.getLooper()));
+ mAppMessenger = new Messenger(mHandler);
+
+ when(mRequestInfo.getPid()).thenReturn(mPid);
+ when(mRequestInfo2.getPid()).thenReturn(mPid2);
+ when(mWifiInjector.getUserManager()).thenReturn(mUserManager);
+ when(mWifiInjector.getWifiController()).thenReturn(mWifiController);
+ when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
+ when(mWifiInjector.getWifiStateMachine()).thenReturn(mWifiStateMachine);
+ when(mWifiStateMachine.syncInitialize(any())).thenReturn(true);
+ when(mWifiInjector.getWifiServiceHandlerThread()).thenReturn(mHandlerThread);
+ when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ doNothing().when(mFrameworkFacade).registerContentObserver(eq(mContext), any(),
+ anyBoolean(), any());
+ when(mContext.getSystemService(Context.ACTIVITY_SERVICE)).thenReturn(mActivityManager);
+ when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
+ when(mFrameworkFacade.getLongSetting(
+ eq(mContext),
+ eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS),
+ anyLong()))
+ .thenReturn(WIFI_BACKGROUND_SCAN_INTERVAL);
+ when(mFrameworkFacade.getStringSetting(
+ eq(mContext),
+ eq(Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST)))
+ .thenReturn(WHITE_LIST_SCAN_PACKAGE_NAME);
+ IPowerManager powerManagerService = mock(IPowerManager.class);
+ mPowerManager = new PowerManager(mContext, powerManagerService, new Handler());
+ when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
+ when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
+ WifiAsyncChannel wifiAsyncChannel = new WifiAsyncChannel("WifiServiceImplTest");
+ wifiAsyncChannel.setWifiLog(mLog);
+ when(mFrameworkFacade.makeWifiAsyncChannel(anyString())).thenReturn(wifiAsyncChannel);
+ when(mWifiInjector.getFrameworkFacade()).thenReturn(mFrameworkFacade);
+ when(mWifiInjector.getWifiLockManager()).thenReturn(mLockManager);
+ when(mWifiInjector.getWifiMulticastLockManager()).thenReturn(mWifiMulticastLockManager);
+ when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
+ when(mWifiInjector.getWifiBackupRestore()).thenReturn(mWifiBackupRestore);
+ when(mWifiInjector.makeLog(anyString())).thenReturn(mLog);
+ WifiTrafficPoller wifiTrafficPoller = new WifiTrafficPoller(mContext,
+ mLooper.getLooper(), "mockWlan");
+ when(mWifiInjector.getWifiTrafficPoller()).thenReturn(wifiTrafficPoller);
+ when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mWifiPermissionsUtil);
+ when(mWifiInjector.getWifiSettingsStore()).thenReturn(mSettingsStore);
+ when(mWifiInjector.getClock()).thenReturn(mClock);
+ when(mWifiInjector.getWifiNotificationController()).thenReturn(mWifiNotificationController);
+ mWifiServiceImpl = new WifiServiceImpl(mContext, mWifiInjector, mAsyncChannel);
+ mWifiServiceImpl.setWifiHandlerLogForTest(mLog);
+ }
+
+ @Test
+ public void testRemoveNetworkUnknown() {
+ assertFalse(mWifiServiceImpl.removeNetwork(-1));
+ }
+
+ @Test
+ public void testAsyncChannelHalfConnected() {
+ WifiAsyncChannelTester channelTester = new WifiAsyncChannelTester(mWifiInjector);
+ Handler handler = mock(Handler.class);
+ TestLooper looper = new TestLooper();
+ channelTester.connect(looper.getLooper(), mWifiServiceImpl.getWifiServiceMessenger(),
+ handler);
+ mLooper.dispatchAll();
+ assertEquals("AsyncChannel must be half connected",
+ WifiAsyncChannelTester.CHANNEL_STATE_HALF_CONNECTED,
+ channelTester.getChannelState());
+ }
+
+ /**
+ * Tests the isValid() check for StaticIpConfigurations, ensuring that configurations with null
+ * ipAddress are rejected, and configurations with ipAddresses are valid.
+ */
+ @Test
+ public void testStaticIpConfigurationValidityCheck() {
+ WifiConfiguration conf = WifiConfigurationTestUtil.createOpenNetwork();
+ IpConfiguration ipConf =
+ WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy();
+ conf.setIpConfiguration(ipConf);
+ // Ensure staticIpConfiguration with IP Address is valid
+ assertTrue(mWifiServiceImpl.isValid(conf));
+ ipConf.staticIpConfiguration.ipAddress = null;
+ // Ensure staticIpConfiguration with null IP Address it is not valid
+ conf.setIpConfiguration(ipConf);
+ assertFalse(mWifiServiceImpl.isValid(conf));
+ }
+
+ /**
+ * Ensure WifiMetrics.dump() is the only dump called when 'dumpsys wifi WifiMetricsProto' is
+ * called. This is required to support simple metrics collection via dumpsys
+ */
+ @Test
+ public void testWifiMetricsDump() {
+ mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()),
+ new String[]{mWifiMetrics.PROTO_DUMP_ARG});
+ verify(mWifiMetrics)
+ .dump(any(FileDescriptor.class), any(PrintWriter.class), any(String[].class));
+ verify(mWifiStateMachine, never())
+ .dump(any(FileDescriptor.class), any(PrintWriter.class), any(String[].class));
+ }
+
+
+ /**
+ * Ensure WifiServiceImpl.dump() doesn't throw an NPE when executed with null args
+ */
+ @Test
+ public void testDumpNullArgs() {
+ mWifiServiceImpl.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null);
+ }
+
+ /**
+ * Verify that wifi can be enabled by a caller with WIFI_STATE_CHANGE permission when wifi is
+ * off (no hotspot, no airplane mode).
+ */
+ @Test
+ public void testSetWifiEnabledSuccess() throws Exception {
+ when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
+ when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+ verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify that the CMD_TOGGLE_WIFI message won't be sent if wifi is already on.
+ */
+ @Test
+ public void testSetWifiEnabledNoToggle() throws Exception {
+ when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
+ when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(false);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+ verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify a SecurityException is thrown if a caller does not have the correct permission to
+ * toggle wifi.
+ */
+ @Test(expected = SecurityException.class)
+ public void testSetWifiEnableWithoutPermission() throws Exception {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
+ eq("WifiService"));
+ mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true);
+ verify(mWifiStateMachine, never()).syncGetWifiApState();
+ }
+
+ /**
+ * Verify that a call from an app with the NETWORK_SETTINGS permission can enable wifi if we
+ * are in softap mode.
+ */
+ @Test
+ public void testSetWifiEnabledFromNetworkSettingsHolderWhenApEnabled() throws Exception {
+ when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
+ when(mSettingsStore.handleWifiToggled(eq(true))).thenReturn(true);
+ when(mContext.checkCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_SETTINGS)))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(SYSUI_PACKAGE_NAME, true));
+ verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify that a call from an app cannot enable wifi if we are in softap mode.
+ */
+ @Test
+ public void testSetWifiEnabledFromAppFailsWhenApEnabled() throws Exception {
+ when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_ENABLED);
+ when(mContext.checkCallingOrSelfPermission(
+ eq(android.Manifest.permission.NETWORK_SETTINGS)))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ assertFalse(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, true));
+ verify(mSettingsStore, never()).handleWifiToggled(anyBoolean());
+ verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify that wifi can be disabled by a caller with WIFI_STATE_CHANGE permission when wifi is
+ * on.
+ */
+ @Test
+ public void testSetWifiDisabledSuccess() throws Exception {
+ when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
+ when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(true);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
+ verify(mWifiController).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify that CMD_TOGGLE_WIFI message won't be sent if wifi is already off.
+ */
+ @Test
+ public void testSetWifiDisabledNoToggle() throws Exception {
+ when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
+ when(mSettingsStore.handleWifiToggled(eq(false))).thenReturn(false);
+ assertTrue(mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false));
+ verify(mWifiController, never()).sendMessage(eq(CMD_WIFI_TOGGLED));
+ }
+
+ /**
+ * Verify a SecurityException is thrown if a caller does not have the correct permission to
+ * toggle wifi.
+ */
+ @Test(expected = SecurityException.class)
+ public void testSetWifiDisabledWithoutPermission() throws Exception {
+ when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
+ eq("WifiService"));
+ mWifiServiceImpl.setWifiEnabled(TEST_PACKAGE_NAME, false);
+ }
+
+ /**
+ * Ensure unpermitted callers cannot write the SoftApConfiguration.
+ *
+ * @throws SecurityException
+ */
+ @Test(expected = SecurityException.class)
+ public void testSetWifiApConfigurationNotSavedWithoutPermission() {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
+ WifiConfiguration apConfig = new WifiConfiguration();
+ mWifiServiceImpl.setWifiApConfiguration(apConfig);
+ verify(mWifiStateMachine, never()).setWifiApConfiguration(eq(apConfig));
+ }
+
+ /**
+ * Ensure softap config is written when the caller has the correct permission.
+ */
+ @Test
+ public void testSetWifiApConfigurationSuccess() {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ WifiConfiguration apConfig = new WifiConfiguration();
+ mWifiServiceImpl.setWifiApConfiguration(apConfig);
+ verify(mWifiStateMachine).setWifiApConfiguration(eq(apConfig));
+ }
+
+ /**
+ * Ensure that a null config does not overwrite the saved ap config.
+ */
+ @Test
+ public void testSetWifiApConfigurationNullConfigNotSaved() {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ mWifiServiceImpl.setWifiApConfiguration(null);
+ verify(mWifiStateMachine, never()).setWifiApConfiguration(isNull(WifiConfiguration.class));
+ }
+
+ /**
+ * Ensure unpermitted callers are not able to retrieve the softap config.
+ *
+ * @throws SecurityException
+ */
+ @Test(expected = SecurityException.class)
+ public void testGetWifiApConfigurationNotReturnedWithoutPermission() {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(false);
+ mWifiServiceImpl.getWifiApConfiguration();
+ verify(mWifiStateMachine, never()).syncGetWifiApConfiguration();
+ }
+
+ /**
+ * Ensure permitted callers are able to retrieve the softap config.
+ */
+ @Test
+ public void testGetWifiApConfigurationSuccess() {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ WifiConfiguration apConfig = new WifiConfiguration();
+ when(mWifiStateMachine.syncGetWifiApConfiguration()).thenReturn(apConfig);
+ assertEquals(apConfig, mWifiServiceImpl.getWifiApConfiguration());
+ }
+
+ /**
+ * Make sure we do not start wifi if System services have to be restarted to decrypt the device.
+ */
+ @Test
+ public void testWifiControllerDoesNotStartWhenDeviceTriggerResetMainAtBoot() {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(true);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+ verify(mWifiController, never()).start();
+ }
+
+ /**
+ * Make sure we do start WifiController (wifi disabled) if the device is already decrypted.
+ */
+ @Test
+ public void testWifiControllerStartsWhenDeviceIsDecryptedAtBootWithWifiDisabled() {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+ verify(mWifiController).start();
+ verify(mWifiController, never()).sendMessage(CMD_WIFI_TOGGLED);
+ }
+
+ /**
+ * Make sure we do start WifiController (wifi enabled) if the device is already decrypted.
+ */
+ @Test
+ public void testWifiFullyStartsWhenDeviceIsDecryptedAtBootWithWifiEnabled() {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.handleWifiToggled(true)).thenReturn(true);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true);
+ when(mWifiStateMachine.syncGetWifiState()).thenReturn(WIFI_STATE_DISABLED);
+ when(mWifiStateMachine.syncGetWifiApState()).thenReturn(WifiManager.WIFI_AP_STATE_DISABLED);
+ when(mContext.getPackageName()).thenReturn(ANDROID_SYSTEM_PACKAGE);
+ mWifiServiceImpl.checkAndStartWifi();
+ verify(mWifiController).start();
+ verify(mWifiController).sendMessage(CMD_WIFI_TOGGLED);
+ }
+
+ /**
+ * Verify setWifiApEnabled works with the correct permissions and a null config.
+ */
+ @Test
+ public void testSetWifiApEnabledWithProperPermissionsWithNullConfig() {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
+ .thenReturn(false);
+ mWifiServiceImpl.setWifiApEnabled(null, true);
+ verify(mWifiController)
+ .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture());
+ assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration());
+ }
+
+ /**
+ * Verify setWifiApEnabled works with correct permissions and a valid config.
+ *
+ * TODO: should really validate that ap configs have a set of basic config settings b/37280779
+ */
+ @Test
+ public void testSetWifiApEnabledWithProperPermissionsWithValidConfig() {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
+ .thenReturn(false);
+ WifiConfiguration apConfig = new WifiConfiguration();
+ mWifiServiceImpl.setWifiApEnabled(apConfig, true);
+ verify(mWifiController).sendMessage(
+ eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture());
+ assertEquals(apConfig, mSoftApModeConfigCaptor.getValue().getWifiConfiguration());
+ }
+
+ /**
+ * Verify setWifiApEnabled when disabling softap with correct permissions sends the correct
+ * message to WifiController.
+ */
+ @Test
+ public void testSetWifiApEnabledFalseWithProperPermissionsWithNullConfig() {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
+ .thenReturn(false);
+ mWifiServiceImpl.setWifiApEnabled(null, false);
+ verify(mWifiController)
+ .sendMessage(eq(CMD_SET_AP), eq(0), eq(0), mSoftApModeConfigCaptor.capture());
+ assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration());
+ }
+
+ /**
+ * setWifiApEnabled should fail if the provided config is not valid.
+ */
+ @Test
+ public void testSetWifiApEnabledWithProperPermissionInvalidConfigFails() {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
+ .thenReturn(false);
+ // mApConfig is a mock and the values are not set - triggering the invalid config. Testing
+ // will be improved when we actually do test softap configs in b/37280779
+ mWifiServiceImpl.setWifiApEnabled(mApConfig, true);
+ verify(mWifiController, never())
+ .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), any(SoftApModeConfiguration.class));
+ }
+
+ /**
+ * setWifiApEnabled should throw a security exception when the caller does not have the correct
+ * permissions.
+ */
+ @Test(expected = SecurityException.class)
+ public void testSetWifiApEnabledThrowsSecurityExceptionWithoutConfigOverridePermission()
+ throws Exception {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
+ eq("WifiService"));
+ mWifiServiceImpl.setWifiApEnabled(null, true);
+ }
+
+ /**
+ * setWifiApEnabled should throw a SecurityException when disallow tethering is set for the
+ * user.
+ */
+ @Test(expected = SecurityException.class)
+ public void testSetWifiApEnabledThrowsSecurityExceptionWithDisallowTethering()
+ throws Exception {
+ when(mWifiPermissionsUtil.checkConfigOverridePermission(anyInt())).thenReturn(true);
+ when(mUserManager.hasUserRestriction(eq(UserManager.DISALLOW_CONFIG_TETHERING)))
+ .thenReturn(true);
+ mWifiServiceImpl.setWifiApEnabled(null, true);
+
+ }
+
+ /**
+ * Verify caller with proper permission can call startSoftAp.
+ */
+ @Test
+ public void testStartSoftApWithPermissionsAndNullConfig() {
+ boolean result = mWifiServiceImpl.startSoftAp(null);
+ assertTrue(result);
+ verify(mWifiController)
+ .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture());
+ assertNull(mSoftApModeConfigCaptor.getValue().getWifiConfiguration());
+ }
+
+ /**
+ * Verify caller with proper permissions but an invalid config does not start softap.
+ */
+ @Test
+ public void testStartSoftApWithPermissionsAndInvalidConfig() {
+ boolean result = mWifiServiceImpl.startSoftAp(mApConfig);
+ assertFalse(result);
+ verifyZeroInteractions(mWifiController);
+ }
+
+ /**
+ * Verify caller with proper permission and valid config does start softap.
+ */
+ @Test
+ public void testStartSoftApWithPermissionsAndValidConfig() {
+ WifiConfiguration config = new WifiConfiguration();
+ boolean result = mWifiServiceImpl.startSoftAp(config);
+ assertTrue(result);
+ verify(mWifiController)
+ .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), mSoftApModeConfigCaptor.capture());
+ assertEquals(config, mSoftApModeConfigCaptor.getValue().getWifiConfiguration());
+ }
+
+ /**
+ * Verify a SecurityException is thrown when a caller without the correct permission attempts to
+ * start softap.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStartSoftApWithoutPermissionThrowsException() throws Exception {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_STACK),
+ eq("WifiService"));
+ mWifiServiceImpl.startSoftAp(null);
+ }
+
+ /**
+ * Verify caller with proper permission can call stopSoftAp.
+ */
+ @Test
+ public void testStopSoftApWithPermissions() {
+ boolean result = mWifiServiceImpl.stopSoftAp();
+ assertTrue(result);
+ verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
+ }
+
+ /**
+ * Verify SecurityException is thrown when a caller without the correct permission attempts to
+ * stop softap.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStopSoftApWithoutPermissionThrowsException() throws Exception {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_STACK),
+ eq("WifiService"));
+ mWifiServiceImpl.stopSoftAp();
+ }
+
+ /**
+ * Ensure foreground apps can always do wifi scans.
+ */
+ @Test
+ public void testWifiScanStartedForeground() {
+ when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+ mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
+ verify(mWifiStateMachine).startScan(
+ anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
+ }
+
+ /**
+ * Ensure background apps get throttled when the previous scan is too close.
+ */
+ @Test
+ public void testWifiScanBackgroundThrottled() {
+ when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
+ long startMs = 1000;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs);
+ mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
+ verify(mWifiStateMachine).startScan(
+ anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000);
+ mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
+ verify(mWifiStateMachine, times(1)).startScan(
+ anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
+ }
+
+ /**
+ * Ensure background apps can do wifi scan when the throttle interval reached.
+ */
+ @Test
+ public void testWifiScanBackgroundNotThrottled() {
+ when(mActivityManager.getPackageImportance(SCAN_PACKAGE_NAME)).thenReturn(
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
+ long startMs = 1000;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs);
+ mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
+ verify(mWifiStateMachine).startScan(
+ anyInt(), eq(0), (ScanSettings) eq(null), any(WorkSource.class));
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ startMs + WIFI_BACKGROUND_SCAN_INTERVAL + 1000);
+ mWifiServiceImpl.startScan(null, null, SCAN_PACKAGE_NAME);
+ verify(mWifiStateMachine).startScan(
+ anyInt(), eq(1), (ScanSettings) eq(null), any(WorkSource.class));
+ }
+
+ /**
+ * Ensure background apps can do wifi scan when the throttle interval reached.
+ */
+ @Test
+ public void testWifiScanBackgroundWhiteListed() {
+ when(mActivityManager.getPackageImportance(WHITE_LIST_SCAN_PACKAGE_NAME)).thenReturn(
+ ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED);
+ long startMs = 1000;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startMs);
+ mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME);
+ verify(mWifiStateMachine).startScan(
+ anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(
+ startMs + WIFI_BACKGROUND_SCAN_INTERVAL - 1000);
+ mWifiServiceImpl.startScan(null, null, WHITE_LIST_SCAN_PACKAGE_NAME);
+ verify(mWifiStateMachine, times(2)).startScan(
+ anyInt(), anyInt(), (ScanSettings) eq(null), any(WorkSource.class));
+ }
+
+ private void registerLOHSRequestFull() {
+ // allow test to proceed without a permission check failure
+ when(mSettingsStore.getLocationModeSetting(mContext))
+ .thenReturn(LOCATION_MODE_HIGH_ACCURACY);
+ try {
+ when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true);
+ } catch (RemoteException e) { }
+ when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING))
+ .thenReturn(false);
+ int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder,
+ TEST_PACKAGE_NAME);
+ assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result);
+ }
+
+ /**
+ * Verify that the call to startLocalOnlyHotspot returns REQUEST_REGISTERED when successfully
+ * called.
+ */
+ @Test
+ public void testStartLocalOnlyHotspotSingleRegistrationReturnsRequestRegistered() {
+ registerLOHSRequestFull();
+ }
+
+ /**
+ * Verify that a call to startLocalOnlyHotspot throws a SecurityException if the caller does not
+ * have the CHANGE_WIFI_STATE permission.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutCorrectPermission() {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
+ eq("WifiService"));
+ mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
+ }
+
+ /**
+ * Verify that a call to startLocalOnlyHotspot throws a SecurityException if the caller does not
+ * have Location permission.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutLocationPermission() {
+ when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ doThrow(new SecurityException())
+ .when(mWifiPermissionsUtil).enforceLocationPermission(eq(TEST_PACKAGE_NAME),
+ anyInt());
+ mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
+ }
+
+ /**
+ * Verify that a call to startLocalOnlyHotspot throws a SecurityException if Location mode is
+ * disabled.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutLocationEnabled() {
+ when(mSettingsStore.getLocationModeSetting(mContext)).thenReturn(LOCATION_MODE_OFF);
+ mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
+ }
+
+ /**
+ * Only start LocalOnlyHotspot if the caller is the foreground app at the time of the request.
+ */
+ @Test
+ public void testStartLocalOnlyHotspotFailsIfRequestorNotForegroundApp() throws Exception {
+ when(mSettingsStore.getLocationModeSetting(mContext))
+ .thenReturn(LOCATION_MODE_HIGH_ACCURACY);
+
+ when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(false);
+ int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder,
+ TEST_PACKAGE_NAME);
+ assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result);
+ }
+
+ /**
+ * Do not register the LocalOnlyHotspot request if the caller app cannot be verified as the
+ * foreground app at the time of the request (ie, throws an exception in the check).
+ */
+ @Test
+ public void testStartLocalOnlyHotspotFailsIfForegroundAppCheckThrowsRemoteException()
+ throws Exception {
+ when(mSettingsStore.getLocationModeSetting(mContext))
+ .thenReturn(LOCATION_MODE_HIGH_ACCURACY);
+
+ when(mFrameworkFacade.isAppForeground(anyInt())).thenThrow(new RemoteException());
+ int result = mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder,
+ TEST_PACKAGE_NAME);
+ assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result);
+ }
+
+ /**
+ * Only start LocalOnlyHotspot if we are not tethering.
+ */
+ @Test
+ public void testHotspotDoesNotStartWhenAlreadyTethering() throws Exception {
+ when(mSettingsStore.getLocationModeSetting(mContext))
+ .thenReturn(LOCATION_MODE_HIGH_ACCURACY);
+ when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true);
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED);
+ mLooper.dispatchAll();
+ int returnCode = mWifiServiceImpl.startLocalOnlyHotspot(
+ mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
+ assertEquals(ERROR_INCOMPATIBLE_MODE, returnCode);
+ }
+
+ /**
+ * Only start LocalOnlyHotspot if admin setting does not disallow tethering.
+ */
+ @Test
+ public void testHotspotDoesNotStartWhenTetheringDisallowed() throws Exception {
+ when(mSettingsStore.getLocationModeSetting(mContext))
+ .thenReturn(LOCATION_MODE_HIGH_ACCURACY);
+ when(mFrameworkFacade.isAppForeground(anyInt())).thenReturn(true);
+ when(mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING))
+ .thenReturn(true);
+ int returnCode = mWifiServiceImpl.startLocalOnlyHotspot(
+ mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
+ assertEquals(ERROR_TETHERING_DISALLOWED, returnCode);
+ }
+
+ /**
+ * Verify that callers can only have one registered LOHS request.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testStartLocalOnlyHotspotThrowsExceptionWhenCallerAlreadyRegistered() {
+ registerLOHSRequestFull();
+
+ // now do the second request that will fail
+ mWifiServiceImpl.startLocalOnlyHotspot(mAppMessenger, mAppBinder, TEST_PACKAGE_NAME);
+ }
+
+ /**
+ * Verify that the call to stopLocalOnlyHotspot does not do anything when there aren't any
+ * registered callers.
+ */
+ @Test
+ public void testStopLocalOnlyHotspotDoesNothingWithoutRegisteredRequests() {
+ // allow test to proceed without a permission check failure
+ mWifiServiceImpl.stopLocalOnlyHotspot();
+ // there is nothing registered, so this shouldn't do anything
+ verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt());
+ }
+
+ /**
+ * Verify that the call to stopLocalOnlyHotspot does not do anything when one caller unregisters
+ * but there is still an active request
+ */
+ @Test
+ public void testStopLocalOnlyHotspotDoesNothingWithARemainingRegisteredRequest() {
+ // register a request that will remain after the stopLOHS call
+ mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo);
+
+ registerLOHSRequestFull();
+
+ // Since we are calling with the same pid, the second register call will be removed
+ mWifiServiceImpl.stopLocalOnlyHotspot();
+ // there is still a valid registered request - do not tear down LOHS
+ verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt());
+ }
+
+ /**
+ * Verify that the call to stopLocalOnlyHotspot sends a message to WifiController to stop
+ * the softAp when there is one registered caller when that caller is removed.
+ */
+ @Test
+ public void testStopLocalOnlyHotspotTriggersSoftApStopWithOneRegisteredRequest() {
+ registerLOHSRequestFull();
+ verify(mWifiController)
+ .sendMessage(eq(CMD_SET_AP), eq(1), eq(0), any(SoftApModeConfiguration.class));
+
+ mWifiServiceImpl.stopLocalOnlyHotspot();
+ // there is was only one request registered, we should tear down softap
+ verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
+ }
+
+ /**
+ * Verify that a call to stopLocalOnlyHotspot throws a SecurityException if the caller does not
+ * have the CHANGE_WIFI_STATE permission.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStopLocalOnlyHotspotThrowsSecurityExceptionWithoutCorrectPermission() {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE),
+ eq("WifiService"));
+ mWifiServiceImpl.stopLocalOnlyHotspot();
+ }
+
+ /**
+ * Verify that WifiServiceImpl does not send the stop ap message if there were no
+ * pending LOHS requests upon a binder death callback.
+ */
+ @Test
+ public void testServiceImplNotCalledWhenBinderDeathTriggeredNoRequests() {
+ LocalOnlyRequestorCallback binderDeathCallback =
+ mWifiServiceImpl.new LocalOnlyRequestorCallback();
+
+ binderDeathCallback.onLocalOnlyHotspotRequestorDeath(mRequestInfo);
+ verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
+ }
+
+ /**
+ * Verify that WifiServiceImpl does not send the stop ap message if there are remaining
+ * registered LOHS requests upon a binder death callback. Additionally verify that softap mode
+ * will be stopped if that remaining request is removed (to verify the binder death properly
+ * cleared the requestor that died).
+ */
+ @Test
+ public void testServiceImplNotCalledWhenBinderDeathTriggeredWithRegisteredRequests() {
+ LocalOnlyRequestorCallback binderDeathCallback =
+ mWifiServiceImpl.new LocalOnlyRequestorCallback();
+
+ // registering a request directly from the test will not trigger a message to start
+ // softap mode
+ mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo);
+
+ registerLOHSRequestFull();
+
+ binderDeathCallback.onLocalOnlyHotspotRequestorDeath(mRequestInfo);
+ verify(mWifiController, never()).sendMessage(eq(CMD_SET_AP), anyInt(), anyInt());
+
+ reset(mWifiController);
+
+ // now stop as the second request and confirm CMD_SET_AP will be sent to make sure binder
+ // death requestor was removed
+ mWifiServiceImpl.stopLocalOnlyHotspot();
+ verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
+ }
+
+ private class IntentFilterMatcher implements ArgumentMatcher<IntentFilter> {
+ @Override
+ public boolean matches(IntentFilter filter) {
+ return filter.hasAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ }
+ }
+
+ /**
+ * Verify that onFailed is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
+ * broadcast is received.
+ */
+ @Test
+ public void testRegisteredCallbacksTriggeredOnSoftApFailureGeneric() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ registerLOHSRequestFull();
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_GENERAL,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_FAILED, message.what);
+ assertEquals(ERROR_GENERIC, message.arg1);
+ }
+
+ /**
+ * Verify that onFailed is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
+ * broadcast is received with the SAP_START_FAILURE_NO_CHANNEL error.
+ */
+ @Test
+ public void testRegisteredCallbacksTriggeredOnSoftApFailureNoChannel() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ registerLOHSRequestFull();
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_FAILED, WIFI_AP_STATE_DISABLED, SAP_START_FAILURE_NO_CHANNEL,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_FAILED, message.what);
+ assertEquals(ERROR_NO_CHANNEL, message.arg1);
+ }
+
+ /**
+ * Verify that onStopped is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
+ * broadcast is received with WIFI_AP_STATE_DISABLING and LOHS was active.
+ */
+ @Test
+ public void testRegisteredCallbacksTriggeredOnSoftApDisabling() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ registerLOHSRequestFull();
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STARTED, message.what);
+ reset(mHandler);
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STOPPED, message.what);
+ }
+
+
+ /**
+ * Verify that onStopped is called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
+ * broadcast is received with WIFI_AP_STATE_DISABLED and LOHS was enabled.
+ */
+ @Test
+ public void testRegisteredCallbacksTriggeredOnSoftApDisabled() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ registerLOHSRequestFull();
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STARTED, message.what);
+ reset(mHandler);
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STOPPED, message.what);
+ }
+
+ /**
+ * Verify that no callbacks are called for registered LOHS callers when a WIFI_AP_STATE_CHANGE
+ * broadcast is received and the softap started.
+ */
+ @Test
+ public void testRegisteredCallbacksNotTriggeredOnSoftApStart() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ registerLOHSRequestFull();
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_ENABLED, WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME,
+ IFACE_IP_MODE_LOCAL_ONLY);
+
+ mLooper.dispatchAll();
+ verifyNoMoreInteractions(mHandler);
+ }
+
+ /**
+ * Verify that onStopped is called only once for registered LOHS callers when
+ * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLING and
+ * WIFI_AP_STATE_DISABLED when LOHS was enabled.
+ */
+ @Test
+ public void testRegisteredCallbacksTriggeredOnlyOnceWhenSoftApDisabling() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ registerLOHSRequestFull();
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STARTED, message.what);
+ reset(mHandler);
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STOPPED, message.what);
+ }
+
+ /**
+ * Verify that onFailed is called only once for registered LOHS callers when
+ * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_FAILED twice.
+ */
+ @Test
+ public void testRegisteredCallbacksTriggeredOnlyOnceWhenSoftApFailsTwice() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ registerLOHSRequestFull();
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_FAILED, message.what);
+ assertEquals(ERROR_GENERIC, message.arg1);
+ }
+
+ /**
+ * Verify that onFailed is called for all registered LOHS callers when
+ * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_FAILED.
+ */
+ @Test
+ public void testAllRegisteredCallbacksTriggeredWhenSoftApFails() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ // make an additional request for this test
+ mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
+
+ registerLOHSRequestFull();
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_FAILED, WIFI_AP_STATE_FAILED, ERROR_GENERIC,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
+ verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC);
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_FAILED, message.what);
+ assertEquals(ERROR_GENERIC, message.arg1);
+ }
+
+ /**
+ * Verify that onStopped is called for all registered LOHS callers when
+ * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLED when LOHS was
+ * active.
+ */
+ @Test
+ public void testAllRegisteredCallbacksTriggeredWhenSoftApStops() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
+
+ registerLOHSRequestFull();
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+ verify(mRequestInfo).sendHotspotStartedMessage(any());
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STARTED, message.what);
+ reset(mHandler);
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
+ verify(mRequestInfo).sendHotspotStoppedMessage();
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STOPPED, message.what);
+ }
+
+ /**
+ * Verify that onFailed is called for all registered LOHS callers when
+ * WIFI_AP_STATE_CHANGE broadcasts are received with WIFI_AP_STATE_DISABLED when LOHS was
+ * not active.
+ */
+ @Test
+ public void testAllRegisteredCallbacksTriggeredWhenSoftApStopsLOHSNotActive() throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
+ mWifiServiceImpl.registerLOHSForTest(TEST_PID2, mRequestInfo2);
+
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+
+ verify(mRequestInfo).sendHotspotFailedMessage(ERROR_GENERIC);
+ verify(mRequestInfo2).sendHotspotFailedMessage(ERROR_GENERIC);
+ }
+
+ /**
+ * Verify that if we do not have registered LOHS requestors and we receive an update that LOHS
+ * is up and ready for use, we tell WifiController to tear it down. This can happen if softap
+ * mode fails to come up properly and we get an onFailed message for a tethering call and we
+ * had registered callers for LOHS.
+ */
+ @Test
+ public void testLOHSReadyWithoutRegisteredRequestsStopsSoftApMode() {
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+
+ verify(mWifiController).sendMessage(eq(CMD_SET_AP), eq(0), eq(0));
+ }
+
+ /**
+ * Verify that all registered LOHS requestors are notified via a HOTSPOT_STARTED message that
+ * the hotspot is up and ready to use.
+ */
+ @Test
+ public void testRegisteredLocalOnlyHotspotRequestorsGetOnStartedCallbackWhenReady()
+ throws Exception {
+ registerLOHSRequestFull();
+
+ mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+ verify(mRequestInfo).sendHotspotStartedMessage(any(WifiConfiguration.class));
+
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STARTED, message.what);
+ assertNotNull((WifiConfiguration) message.obj);
+ }
+
+ /**
+ * Verify that if a LOHS is already active, a new call to register a request will trigger the
+ * onStarted callback.
+ */
+ @Test
+ public void testRegisterLocalOnlyHotspotRequestAfterAlreadyStartedGetsOnStartedCallback()
+ throws Exception {
+ mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+
+ registerLOHSRequestFull();
+
+ mLooper.dispatchAll();
+
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_STARTED, message.what);
+ // since the first request was registered out of band, the config will be null
+ assertNull((WifiConfiguration) message.obj);
+ }
+
+ /**
+ * Verify that if a LOHS request is active and we receive an update with an ip mode
+ * configuration error, callers are notified via the onFailed callback with the generic
+ * error and are unregistered.
+ */
+ @Test
+ public void testCallOnFailedLocalOnlyHotspotRequestWhenIpConfigFails() throws Exception {
+ registerLOHSRequestFull();
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_CONFIGURATION_ERROR);
+ mLooper.dispatchAll();
+
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_FAILED, message.what);
+ assertEquals(ERROR_GENERIC, message.arg1);
+
+ // sendMessage should only happen once since the requestor should be unregistered
+ reset(mHandler);
+
+ // send HOTSPOT_FAILED message should only happen once since the requestor should be
+ // unregistered
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_CONFIGURATION_ERROR);
+ mLooper.dispatchAll();
+ verify(mHandler, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Verify that if a LOHS request is active and tethering starts, callers are notified on the
+ * incompatible mode and are unregistered.
+ */
+ @Test
+ public void testCallOnFailedLocalOnlyHotspotRequestWhenTetheringStarts() throws Exception {
+ registerLOHSRequestFull();
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED);
+ mLooper.dispatchAll();
+
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ Message message = mMessageCaptor.getValue();
+ assertEquals(HOTSPOT_FAILED, message.what);
+ assertEquals(ERROR_INCOMPATIBLE_MODE, message.arg1);
+
+ // sendMessage should only happen once since the requestor should be unregistered
+ reset(mHandler);
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_TETHERED);
+ mLooper.dispatchAll();
+ verify(mHandler, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Verify that if LOHS is disabled, a new call to register a request will not trigger the
+ * onStopped callback.
+ */
+ @Test
+ public void testRegisterLocalOnlyHotspotRequestWhenStoppedDoesNotGetOnStoppedCallback()
+ throws Exception {
+ registerLOHSRequestFull();
+ mLooper.dispatchAll();
+
+ verify(mHandler, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Verify that if a LOHS was active and then stopped, a new call to register a request will
+ * not trigger the onStarted callback.
+ */
+ @Test
+ public void testRegisterLocalOnlyHotspotRequestAfterStoppedNoOnStartedCallback()
+ throws Exception {
+ when(mFrameworkFacade.inStorageManagerCryptKeeperBounce()).thenReturn(false);
+ when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false);
+ mWifiServiceImpl.checkAndStartWifi();
+ verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ (IntentFilter) argThat(new IntentFilterMatcher()));
+
+ // register a request so we don't drop the LOHS interface ip update
+ mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo);
+
+ mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+
+ registerLOHSRequestFull();
+ mLooper.dispatchAll();
+
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ assertEquals(HOTSPOT_STARTED, mMessageCaptor.getValue().what);
+
+ reset(mHandler);
+
+ // now stop the hotspot
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLING, WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ TestUtil.sendWifiApStateChanged(mBroadcastReceiverCaptor.getValue(), mContext,
+ WIFI_AP_STATE_DISABLED, WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR,
+ WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY);
+ mLooper.dispatchAll();
+ verify(mHandler).handleMessage(mMessageCaptor.capture());
+ assertEquals(HOTSPOT_STOPPED, mMessageCaptor.getValue().what);
+
+ reset(mHandler);
+
+ // now register a new caller - they should not get the onStarted callback
+ Messenger messenger2 = new Messenger(mHandler);
+ IBinder binder2 = mock(IBinder.class);
+
+ int result = mWifiServiceImpl.startLocalOnlyHotspot(messenger2, binder2, TEST_PACKAGE_NAME);
+ assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result);
+ mLooper.dispatchAll();
+
+ verify(mHandler, never()).handleMessage(any(Message.class));
+ }
+
+ /**
+ * Verify that a call to startWatchLocalOnlyHotspot is only allowed from callers with the
+ * signature only NETWORK_SETTINGS permission.
+ *
+ * This test is expecting the permission check to enforce the permission and throw a
+ * SecurityException for callers without the permission. This exception should be bubbled up to
+ * the caller of startLocalOnlyHotspot.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStartWatchLocalOnlyHotspotNotApprovedCaller() {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+ eq("WifiService"));
+ mWifiServiceImpl.startWatchLocalOnlyHotspot(mAppMessenger, mAppBinder);
+ }
+
+ /**
+ * Verify that the call to startWatchLocalOnlyHotspot throws the UnsupportedOperationException
+ * when called until the implementation is complete.
+ */
+ @Test(expected = UnsupportedOperationException.class)
+ public void testStartWatchLocalOnlyHotspotNotSupported() {
+ mWifiServiceImpl.startWatchLocalOnlyHotspot(mAppMessenger, mAppBinder);
+ }
+
+ /**
+ * Verify that a call to stopWatchLocalOnlyHotspot is only allowed from callers with the
+ * signature only NETWORK_SETTINGS permission.
+ */
+ @Test(expected = SecurityException.class)
+ public void testStopWatchLocalOnlyHotspotNotApprovedCaller() {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+ eq("WifiService"));
+ mWifiServiceImpl.stopWatchLocalOnlyHotspot();
+ }
+
+ /**
+ * Verify that the call to stopWatchLocalOnlyHotspot throws the UnsupportedOperationException
+ * until the implementation is complete.
+ */
+ @Test(expected = UnsupportedOperationException.class)
+ public void testStopWatchLocalOnlyHotspotNotSupported() {
+ mWifiServiceImpl.stopWatchLocalOnlyHotspot();
+ }
+
+ /**
+ * Verify that the call to addOrUpdateNetwork for installing Passpoint profile is redirected
+ * to the Passpoint specific API addOrUpdatePasspointConfiguration.
+ */
+ @Test
+ public void testAddPasspointProfileViaAddNetwork() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPasspointNetwork();
+ config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ when(mResources.getBoolean(com.android.internal.R.bool.config_wifi_hotspot2_enabled))
+ .thenReturn(true);
+
+ when(mWifiStateMachine.syncAddOrUpdatePasspointConfig(any(),
+ any(PasspointConfiguration.class), anyInt())).thenReturn(true);
+ assertEquals(0, mWifiServiceImpl.addOrUpdateNetwork(config));
+ verify(mWifiStateMachine).syncAddOrUpdatePasspointConfig(any(),
+ any(PasspointConfiguration.class), anyInt());
+ reset(mWifiStateMachine);
+
+ when(mWifiStateMachine.syncAddOrUpdatePasspointConfig(any(),
+ any(PasspointConfiguration.class), anyInt())).thenReturn(false);
+ assertEquals(-1, mWifiServiceImpl.addOrUpdateNetwork(config));
+ verify(mWifiStateMachine).syncAddOrUpdatePasspointConfig(any(),
+ any(PasspointConfiguration.class), anyInt());
+ }
+
+ /**
+ * Verify that a call to {@link WifiServiceImpl#restoreBackupData(byte[])} is only allowed from
+ * callers with the signature only NETWORK_SETTINGS permission.
+ */
+ @Test(expected = SecurityException.class)
+ public void testRestoreBackupDataNotApprovedCaller() {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+ eq("WifiService"));
+ mWifiServiceImpl.restoreBackupData(null);
+ verify(mWifiBackupRestore, never()).retrieveConfigurationsFromBackupData(any(byte[].class));
+ }
+
+ /**
+ * Verify that a call to {@link WifiServiceImpl#restoreSupplicantBackupData(byte[], byte[])} is
+ * only allowed from callers with the signature only NETWORK_SETTINGS permission.
+ */
+ @Test(expected = SecurityException.class)
+ public void testRestoreSupplicantBackupDataNotApprovedCaller() {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+ eq("WifiService"));
+ mWifiServiceImpl.restoreSupplicantBackupData(null, null);
+ verify(mWifiBackupRestore, never()).retrieveConfigurationsFromSupplicantBackupData(
+ any(byte[].class), any(byte[].class));
+ }
+
+ /**
+ * Verify that a call to {@link WifiServiceImpl#retrieveBackupData()} is only allowed from
+ * callers with the signature only NETWORK_SETTINGS permission.
+ */
+ @Test(expected = SecurityException.class)
+ public void testRetrieveBackupDataNotApprovedCaller() {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(android.Manifest.permission.NETWORK_SETTINGS),
+ eq("WifiService"));
+ mWifiServiceImpl.retrieveBackupData();
+ verify(mWifiBackupRestore, never()).retrieveBackupDataFromConfigurations(any(List.class));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
new file mode 100644
index 0000000..49c7d18
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachinePrimeTest.java
@@ -0,0 +1,392 @@
+/*
+ * 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 static org.mockito.Mockito.*;
+
+import android.net.wifi.IApInterface;
+import android.net.wifi.IWificond;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.INetworkManagementService;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiStateMachinePrime}.
+ */
+@SmallTest
+public class WifiStateMachinePrimeTest {
+ public static final String TAG = "WifiStateMachinePrimeTest";
+
+ private static final String CLIENT_MODE_STATE_STRING = "ClientModeState";
+ private static final String SCAN_ONLY_MODE_STATE_STRING = "ScanOnlyModeState";
+ private static final String SOFT_AP_MODE_STATE_STRING = "SoftAPModeState";
+ private static final String WIFI_DISABLED_STATE_STRING = "WifiDisabledState";
+ private static final String CLIENT_MODE_ACTIVE_STATE_STRING = "ClientModeActiveState";
+ private static final String SCAN_ONLY_MODE_ACTIVE_STATE_STRING = "ScanOnlyModeActiveState";
+ private static final String SOFT_AP_MODE_ACTIVE_STATE_STRING = "SoftAPModeActiveState";
+
+ @Mock WifiInjector mWifiInjector;
+ @Mock WifiApConfigStore mWifiApConfigStore;
+ TestLooper mLooper;
+ @Mock IWificond mWificond;
+ @Mock IApInterface mApInterface;
+ @Mock INetworkManagementService mNMService;
+ @Mock SoftApManager mSoftApManager;
+ SoftApManager.Listener mSoftApListener;
+ WifiStateMachinePrime mWifiStateMachinePrime;
+
+ /**
+ * Set up the test environment.
+ */
+ @Before
+ public void setUp() throws Exception {
+ Log.d(TAG, "Setting up ...");
+
+ MockitoAnnotations.initMocks(this);
+ mLooper = new TestLooper();
+
+ mWifiInjector = mock(WifiInjector.class);
+ mWifiStateMachinePrime = createWifiStateMachinePrime();
+ }
+
+ private WifiStateMachinePrime createWifiStateMachinePrime() {
+ when(mWifiInjector.makeWificond()).thenReturn(null);
+ return new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
+ }
+
+ /**
+ * Clean up after tests - explicitly set tested object to null.
+ */
+ @After
+ public void cleanUp() throws Exception {
+ mWifiStateMachinePrime = null;
+ }
+
+ private void enterSoftApActiveMode() throws Exception {
+ enterSoftApActiveMode(null);
+ }
+
+ /**
+ * Helper method to enter the SoftApActiveMode for WifiStateMachinePrime.
+ *
+ * This method puts the test object into the correct state and verifies steps along the way.
+ */
+ private void enterSoftApActiveMode(WifiConfiguration wifiConfig) throws Exception {
+ String fromState = mWifiStateMachinePrime.getCurrentMode();
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ when(mWificond.createApInterface()).thenReturn(mApInterface);
+ doAnswer(
+ new Answer<Object>() {
+ public SoftApManager answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ assertEquals(mNMService, (INetworkManagementService) args[0]);
+ mSoftApListener = (SoftApManager.Listener) args[1];
+ assertEquals(mApInterface, (IApInterface) args[2]);
+ assertEquals(wifiConfig, (WifiConfiguration) args[3]);
+ return mSoftApManager;
+ }
+ }).when(mWifiInjector).makeSoftApManager(any(INetworkManagementService.class),
+ any(SoftApManager.Listener.class),
+ any(IApInterface.class),
+ any());
+ mWifiStateMachinePrime.enterSoftAPMode(wifiConfig);
+ mLooper.dispatchAll();
+ Log.e("WifiStateMachinePrimeTest", "check fromState: " + fromState);
+ if (!fromState.equals(WIFI_DISABLED_STATE_STRING)) {
+ verify(mWificond).tearDownInterfaces();
+ }
+ assertEquals(SOFT_AP_MODE_ACTIVE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ verify(mSoftApManager).start();
+ }
+
+ /**
+ * Test that when a new instance of WifiStateMachinePrime is created, any existing interfaces in
+ * the retrieved Wificond instance are cleaned up.
+ * Expectations: When the new WifiStateMachinePrime instance is created a call to
+ * Wificond.tearDownInterfaces() is made.
+ */
+ @Test
+ public void testWificondExistsOnStartup() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ WifiStateMachinePrime testWifiStateMachinePrime =
+ new WifiStateMachinePrime(mWifiInjector, mLooper.getLooper(), mNMService);
+ verify(mWificond).tearDownInterfaces();
+ }
+
+ /**
+ * Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from the
+ * WifiDisabled state.
+ */
+ @Test
+ public void testEnterSoftApModeFromDisabled() throws Exception {
+ enterSoftApActiveMode();
+ }
+
+ /**
+ * Test that WifiStateMachinePrime properly enters the SoftApModeActiveState from another state.
+ * Expectations: When going from one state to another, any interfaces that are still up are torn
+ * down.
+ */
+ @Test
+ public void testEnterSoftApModeFromDifferentState() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ mWifiStateMachinePrime.enterClientMode();
+ mLooper.dispatchAll();
+ assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ enterSoftApActiveMode();
+ }
+
+ /**
+ * Test that we can disable wifi fully from the SoftApModeActiveState.
+ */
+ @Test
+ public void testDisableWifiFromSoftApModeActiveState() throws Exception {
+ enterSoftApActiveMode();
+
+ mWifiStateMachinePrime.disableWifi();
+ mLooper.dispatchAll();
+ verify(mSoftApManager).stop();
+ verify(mWificond).tearDownInterfaces();
+ assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we can disable wifi fully from the SoftApModeState.
+ */
+ @Test
+ public void testDisableWifiFromSoftApModeState() throws Exception {
+ // Use a failure getting wificond to stay in the SoftAPModeState
+ when(mWifiInjector.makeWificond()).thenReturn(null);
+ mWifiStateMachinePrime.enterSoftAPMode(null);
+ mLooper.dispatchAll();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+
+ mWifiStateMachinePrime.disableWifi();
+ mLooper.dispatchAll();
+ // mWificond will be null due to this test, no call to tearDownInterfaces here.
+ assertEquals(WIFI_DISABLED_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we can switch from SoftApActiveMode to another mode.
+ * Expectation: When switching out of SoftApModeActiveState we stop the SoftApManager and tear
+ * down existing interfaces.
+ */
+ @Test
+ public void testSwitchModeWhenSoftApActiveMode() throws Exception {
+ enterSoftApActiveMode();
+
+ mWifiStateMachinePrime.enterClientMode();
+ mLooper.dispatchAll();
+ verify(mSoftApManager).stop();
+ verify(mWificond).tearDownInterfaces();
+ assertEquals(CLIENT_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we do not attempt to enter SoftApModeActiveState when we cannot get a reference to
+ * wificond.
+ * Expectations: After a failed attempt to get wificond from WifiInjector, we should remain in
+ * the SoftApModeState.
+ */
+ @Test
+ public void testWificondNullWhenSwitchingToApMode() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(null);
+ mWifiStateMachinePrime.enterSoftAPMode(null);
+ mLooper.dispatchAll();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we do not attempt to enter SoftApModeActiveState when we cannot get an ApInterface
+ * from wificond.
+ * Expectations: After a failed attempt to get an ApInterface from WifiInjector, we should
+ * remain in the SoftApModeState.
+ */
+ @Test
+ public void testAPInterfaceFailedWhenSwitchingToApMode() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ when(mWificond.createApInterface()).thenReturn(null);
+ mWifiStateMachinePrime.enterSoftAPMode(null);
+ mLooper.dispatchAll();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we do can enter the SoftApModeActiveState if we are already in the SoftApModeState.
+ * Expectations: We should exit the current SoftApModeState and re-enter before successfully
+ * entering the SoftApModeActiveState.
+ */
+ @Test
+ public void testEnterSoftApModeActiveWhenAlreadyInSoftApMode() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ when(mWificond.createApInterface()).thenReturn(null);
+ mWifiStateMachinePrime.enterSoftAPMode(null);
+ mLooper.dispatchAll();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+
+ enterSoftApActiveMode();
+ }
+
+ /**
+ * Test that we return to the SoftApModeState after a failure is reported when in the
+ * SoftApModeActiveState.
+ * Expectations: We should exit the SoftApModeActiveState and stop the SoftApManager.
+ */
+ @Test
+ public void testSoftApFailureWhenActive() throws Exception {
+ enterSoftApActiveMode();
+ // now inject failure through the SoftApManager.Listener
+ mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0);
+ mLooper.dispatchAll();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ verify(mSoftApManager).stop();
+ }
+
+ /**
+ * Test that we return to the SoftApModeState after the SoftApManager is stopped in the
+ * SoftApModeActiveState.
+ * Expectations: We should exit the SoftApModeActiveState and stop the SoftApManager.
+ */
+ @Test
+ public void testSoftApDisabledWhenActive() throws Exception {
+ enterSoftApActiveMode();
+ // now inject failure through the SoftApManager.Listener
+ mSoftApListener.onStateChanged(WifiManager.WIFI_AP_STATE_FAILED, 0);
+ mLooper.dispatchAll();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ verify(mSoftApManager).stop();
+ }
+
+ /**
+ * Test that a config passed in to the call to enterSoftApMode is used to create the new
+ * SoftApManager.
+ * Expectations: We should create a SoftApManager in WifiInjector with the config passed in to
+ * WifiStateMachinePrime to switch to SoftApMode.
+ */
+ @Test
+ public void testConfigIsPassedToWifiInjector() throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "ThisIsAConfig";
+ enterSoftApActiveMode(config);
+ }
+
+ /**
+ * Test that when enterSoftAPMode is called with a null config, we pass a null config to
+ * WifiInjector.makeSoftApManager.
+ *
+ * Passing a null config to SoftApManager indicates that the default config should be used.
+ *
+ * Expectations: WifiInjector should be called with a null config.
+ */
+ @Test
+ public void testNullConfigIsPassedToWifiInjector() throws Exception {
+ enterSoftApActiveMode(null);
+ }
+
+ /**
+ * Test that the proper config is used if a prior attempt fails without using the config.
+ * Expectations: A call to start softap with a null config fails, but a second call has a set
+ * config - this second call should use the correct config.
+ */
+ @Test
+ public void testNullConfigFailsSecondCallWithConfigSuccessful() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ when(mWificond.createApInterface()).thenReturn(null);
+ mWifiStateMachinePrime.enterSoftAPMode(null);
+ mLooper.dispatchAll();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "ThisIsAConfig";
+ enterSoftApActiveMode(config);
+ }
+
+ /**
+ * Test that a failed call to start softap with a valid config has the config saved for future
+ * calls to enable softap.
+ *
+ * Expectations: A call to start SoftAPMode with a config should write out the config if we
+ * did not create a SoftApManager.
+ */
+ @Test
+ public void testValidConfigIsSavedOnFailureToStart() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(null);
+ when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore);
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "ThisIsAConfig";
+ mWifiStateMachinePrime.enterSoftAPMode(config);
+ mLooper.dispatchAll();
+ assertEquals(SOFT_AP_MODE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ verify(mWifiApConfigStore).setApConfiguration(eq(config));
+ }
+
+ /**
+ * Thest that two calls to switch to SoftAPMode in succession ends up with the correct config.
+ *
+ * Expectation: we should end up in SoftAPMode state configured with the second config.
+ */
+ @Test
+ public void testStartSoftApModeTwiceWithTwoConfigs() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ when(mWificond.createApInterface()).thenReturn(mApInterface);
+ when(mWifiInjector.getWifiApConfigStore()).thenReturn(mWifiApConfigStore);
+ WifiConfiguration config1 = new WifiConfiguration();
+ config1.SSID = "ThisIsAConfig";
+ WifiConfiguration config2 = new WifiConfiguration();
+ config2.SSID = "ThisIsASecondConfig";
+
+ when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class),
+ any(SoftApManager.Listener.class),
+ any(IApInterface.class),
+ eq(config1)))
+ .thenReturn(mSoftApManager);
+ when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class),
+ any(SoftApManager.Listener.class),
+ any(IApInterface.class),
+ eq(config2)))
+ .thenReturn(mSoftApManager);
+
+
+ mWifiStateMachinePrime.enterSoftAPMode(config1);
+ mWifiStateMachinePrime.enterSoftAPMode(config2);
+ mLooper.dispatchAll();
+ assertEquals(SOFT_AP_MODE_ACTIVE_STATE_STRING, mWifiStateMachinePrime.getCurrentMode());
+ }
+
+ /**
+ * Test that we safely disable wifi if it is already disabled.
+ * Expectations: We should not interact with wificond since we should have already cleaned up
+ * everything.
+ */
+ @Test
+ public void disableWifiWhenAlreadyOff() throws Exception {
+ verifyNoMoreInteractions(mWificond);
+ mWifiStateMachinePrime.disableWifi();
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index f3fe0cd..2a30b67 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -16,15 +16,29 @@
package com.android.server.wifi;
+import static android.net.wifi.WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_FAILURE_REASON;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
+
+import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import android.app.ActivityManager;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.app.test.TestAlarmManager;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
@@ -33,12 +47,19 @@
import android.net.LinkProperties;
import android.net.dhcp.DhcpClient;
import android.net.ip.IpManager;
+import android.net.wifi.IApInterface;
+import android.net.wifi.IClientInterface;
+import android.net.wifi.IWificond;
import android.net.wifi.ScanResult;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
import android.net.wifi.WifiSsid;
+import android.net.wifi.WpsInfo;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.HomeSp;
import android.net.wifi.p2p.IWifiP2pManager;
import android.os.BatteryStats;
import android.os.Binder;
@@ -53,31 +74,33 @@
import android.os.Message;
import android.os.Messenger;
import android.os.PowerManager;
+import android.os.RemoteException;
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;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Base64;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.app.IBatteryStats;
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.hotspot2.PasspointManager;
import com.android.server.wifi.p2p.WifiP2pServiceImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -88,11 +111,11 @@
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
/**
* Unit tests for {@link com.android.server.wifi.WifiStateMachine}.
@@ -107,6 +130,8 @@
(ActivityManager.isLowRamDeviceStatic()
? WifiStateMachine.NUM_LOG_RECS_VERBOSE_LOW_MEMORY
: WifiStateMachine.NUM_LOG_RECS_VERBOSE);
+ private static final int WPS_SUPPLICANT_NETWORK_ID = 5;
+ private static final int WPS_FRAMEWORK_NETWORK_ID = 10;
private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\"";
private long mBinderToken;
@@ -157,9 +182,6 @@
private FrameworkFacade getFrameworkFacade() throws Exception {
FrameworkFacade facade = mock(FrameworkFacade.class);
- when(facade.makeWifiScanner(any(Context.class), any(Looper.class)))
- .thenReturn(mWifiScanner);
- when(facade.makeBaseLogger()).thenReturn(mock(BaseWifiLogger.class));
when(facade.getService(Context.NETWORKMANAGEMENT_SERVICE)).thenReturn(
mockWithInterfaces(IBinder.class, INetworkManagementService.class));
@@ -169,20 +191,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));
@@ -199,20 +217,6 @@
}
});
- when(facade.checkUidPermission(eq(android.Manifest.permission.OVERRIDE_WIFI_CONFIG),
- anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
-
- when(facade.makeWifiConfigManager(any(Context.class), any(WifiNative.class),
- any(FrameworkFacade.class), any(Clock.class),
- any(UserManager.class), any(KeyStore.class))).then(new AnswerWithArguments() {
- public WifiConfigManager answer(Context context, WifiNative wifiNative,
- FrameworkFacade frameworkFacade, Clock clock,
- UserManager userManager, KeyStore keyStore){
- mWifiConfigManager = new WifiConfigManager(context, wifiNative, frameworkFacade,
- clock, userManager, keyStore);
- return mWifiConfigManager;
- }
- });
return facade;
}
@@ -239,7 +243,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());
@@ -311,19 +315,22 @@
static final String sHexSSID = sWifiSsid.getHexString().replace("0x", "").replace("22", "");
static final String sBSSID = "01:02:03:04:05:06";
static final int sFreq = 2437;
+ static final String WIFI_IFACE_NAME = "mockWlan";
WifiStateMachine mWsm;
HandlerThread mWsmThread;
HandlerThread mP2pThread;
HandlerThread mSyncThread;
AsyncChannel mWsmAsyncChannel;
- MockAlarmManager mAlarmManager;
+ TestAlarmManager mAlarmManager;
MockWifiMonitor mWifiMonitor;
TestIpManager mTestIpManager;
- MockLooper mLooper;
- WifiConfigManager mWifiConfigManager;
+ TestLooper mLooper;
+ Context mContext;
- @Mock WifiNative mWifiNative;
+ final ArgumentCaptor<SoftApManager.Listener> mSoftApManagerListenerCaptor =
+ ArgumentCaptor.forClass(SoftApManager.Listener.class);
+
@Mock WifiScanner mWifiScanner;
@Mock SupplicantStateTracker mSupplicantStateTracker;
@Mock WifiMetrics mWifiMetrics;
@@ -335,6 +342,18 @@
@Mock WifiLastResortWatchdog mWifiLastResortWatchdog;
@Mock PropertyService mPropertyService;
@Mock BuildProperties mBuildProperties;
+ @Mock IWificond mWificond;
+ @Mock IApInterface mApInterface;
+ @Mock IClientInterface mClientInterface;
+ @Mock IBinder mApInterfaceBinder;
+ @Mock IBinder mClientInterfaceBinder;
+ @Mock WifiConfigManager mWifiConfigManager;
+ @Mock WifiNative mWifiNative;
+ @Mock WifiConnectivityManager mWifiConnectivityManager;
+ @Mock SoftApManager mSoftApManager;
+ @Mock WifiStateTracker mWifiStateTracker;
+ @Mock PasspointManager mPasspointManager;
+ @Mock SelfRecovery mSelfRecovery;
public WifiStateMachineTest() throws Exception {
}
@@ -344,33 +363,59 @@
Log.d(TAG, "Setting up ...");
// Ensure looper exists
- mLooper = new MockLooper();
+ mLooper = new TestLooper();
MockitoAnnotations.initMocks(this);
/** uncomment this to enable logs from WifiStateMachines */
// enableDebugLogs();
- TestUtil.installWlanWifiNative(mWifiNative);
mWifiMonitor = new MockWifiMonitor();
when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
- when(mWifiInjector.getClock()).thenReturn(mock(Clock.class));
+ when(mWifiInjector.getClock()).thenReturn(new Clock());
when(mWifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
when(mWifiInjector.getPropertyService()).thenReturn(mPropertyService);
when(mWifiInjector.getBuildProperties()).thenReturn(mBuildProperties);
when(mWifiInjector.getKeyStore()).thenReturn(mock(KeyStore.class));
+ when(mWifiInjector.getWifiBackupRestore()).thenReturn(mock(WifiBackupRestore.class));
+ when(mWifiInjector.makeWifiDiagnostics(anyObject())).thenReturn(
+ mock(BaseWifiDiagnostics.class));
+ when(mWifiInjector.makeWificond()).thenReturn(mWificond);
+ when(mWifiInjector.getWifiConfigManager()).thenReturn(mWifiConfigManager);
+ when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
+ when(mWifiInjector.makeWifiConnectivityManager(any(WifiInfo.class), anyBoolean()))
+ .thenReturn(mWifiConnectivityManager);
+ when(mWifiInjector.makeSoftApManager(any(INetworkManagementService.class),
+ mSoftApManagerListenerCaptor.capture(), any(IApInterface.class),
+ any(WifiConfiguration.class)))
+ .thenReturn(mSoftApManager);
+ when(mWifiInjector.getPasspointManager()).thenReturn(mPasspointManager);
+ when(mWifiInjector.getWifiStateTracker()).thenReturn(mWifiStateTracker);
+ when(mWifiInjector.getWifiMonitor()).thenReturn(mWifiMonitor);
+ when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
+ when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery);
+
+ when(mWifiNative.setupForClientMode()).thenReturn(mClientInterface);
+ when(mWifiNative.setupForSoftApMode()).thenReturn(mApInterface);
+ when(mApInterface.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
+ when(mWifiNative.getInterfaceName()).thenReturn(WIFI_IFACE_NAME);
+ when(mWifiNative.enableSupplicant()).thenReturn(true);
+ when(mWifiNative.disableSupplicant()).thenReturn(true);
+ when(mWifiNative.getFrameworkNetworkId(anyInt())).thenReturn(0);
+
+
FrameworkFacade factory = getFrameworkFacade();
- Context context = getContext();
+ mContext = getContext();
Resources resources = getMockResources();
- when(context.getResources()).thenReturn(resources);
+ when(mContext.getResources()).thenReturn(resources);
- when(factory.getIntegerSetting(context,
+ when(factory.getIntegerSetting(mContext,
Settings.Global.WIFI_FREQUENCY_BAND,
WifiManager.WIFI_FREQUENCY_BAND_AUTO)).thenReturn(
WifiManager.WIFI_FREQUENCY_BAND_AUTO);
- when(factory.makeApConfigStore(eq(context), eq(mBackupManagerProxy)))
+ when(factory.makeApConfigStore(eq(mContext), eq(mBackupManagerProxy)))
.thenReturn(mApConfigStore);
when(factory.makeSupplicantStateTracker(
@@ -383,8 +428,11 @@
new UserInfo(UserHandle.USER_SYSTEM, "owner", 0),
new UserInfo(11, "managed profile", 0)));
- mWsm = new WifiStateMachine(context, factory, mLooper.getLooper(),
- mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode);
+ when(mApInterface.asBinder()).thenReturn(mApInterfaceBinder);
+ when(mClientInterface.asBinder()).thenReturn(mClientInterfaceBinder);
+
+ mWsm = new WifiStateMachine(mContext, factory, mLooper.getLooper(),
+ mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative);
mWsmThread = getWsmHandlerThread(mWsm);
final AsyncChannel channel = new AsyncChannel();
@@ -406,7 +454,7 @@
}
};
- channel.connect(context, handler, mWsm.getMessenger());
+ channel.connect(mContext, handler, mWsm.getMessenger());
mLooper.dispatchAll();
/* Now channel is supposed to be connected */
@@ -438,79 +486,217 @@
}
@Test
- public void loadComponents() throws Exception {
- when(mWifiNative.loadDriver()).thenReturn(true);
- when(mWifiNative.startHal()).thenReturn(true);
- when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(true);
- mWsm.setSupplicantRunning(true);
- mLooper.dispatchAll();
+ public void loadComponentsInStaMode() throws Exception {
+ startSupplicantAndDispatchMessages();
- assertEquals("SupplicantStartingState", getCurrentState().getName());
-
- when(mWifiNative.setBand(anyInt())).thenReturn(true);
- when(mWifiNative.setDeviceName(anyString())).thenReturn(true);
- when(mWifiNative.setManufacturer(anyString())).thenReturn(true);
- when(mWifiNative.setModelName(anyString())).thenReturn(true);
- when(mWifiNative.setModelNumber(anyString())).thenReturn(true);
- when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
- when(mWifiNative.setConfigMethods(anyString())).thenReturn(true);
- when(mWifiNative.setDeviceType(anyString())).thenReturn(true);
- when(mWifiNative.setSerialNumber(anyString())).thenReturn(true);
- when(mWifiNative.setScanningMacOui(any(byte[].class))).thenReturn(true);
-
- mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
- mLooper.dispatchAll();
+ verify(mContext).sendStickyBroadcastAsUser(
+ (Intent) argThat(new WifiEnablingStateIntentMatcher()), eq(UserHandle.ALL));
assertEquals("DisconnectedState", getCurrentState().getName());
}
+ private void checkApStateChangedBroadcast(Intent intent, int expectedCurrentState,
+ int expectedPrevState, int expectedErrorCode, String expectedIfaceName,
+ int expectedMode) {
+ int currentState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
+ int prevState = intent.getIntExtra(EXTRA_PREVIOUS_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
+ int errorCode = intent.getIntExtra(EXTRA_WIFI_AP_FAILURE_REASON, HOTSPOT_NO_ERROR);
+ String ifaceName = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);
+ int mode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ assertEquals(expectedCurrentState, currentState);
+ assertEquals(expectedPrevState, prevState);
+ assertEquals(expectedErrorCode, errorCode);
+ assertEquals(expectedIfaceName, ifaceName);
+ assertEquals(expectedMode, mode);
+ }
+
+ private void loadComponentsInApMode(int mode) throws Exception {
+ SoftApModeConfiguration config = new SoftApModeConfiguration(mode, new WifiConfiguration());
+ mWsm.setHostApRunning(config, true);
+ mLooper.dispatchAll();
+
+ assertEquals("SoftApState", getCurrentState().getName());
+
+ verify(mSoftApManager).start();
+
+ // reset expectations for mContext due to previously sent AP broadcast
+ reset(mContext);
+
+ // get the SoftApManager.Listener and trigger some updates
+ SoftApManager.Listener listener = mSoftApManagerListenerCaptor.getValue();
+ listener.onStateChanged(WIFI_AP_STATE_ENABLING, 0);
+ listener.onStateChanged(WIFI_AP_STATE_ENABLED, 0);
+ listener.onStateChanged(WIFI_AP_STATE_DISABLING, 0);
+ // note, this will trigger a mode change when TestLooper is dispatched
+ listener.onStateChanged(WIFI_AP_STATE_DISABLED, 0);
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(4))
+ .sendStickyBroadcastAsUser(intentCaptor.capture(), eq(UserHandle.ALL));
+
+ List<Intent> capturedIntents = intentCaptor.getAllValues();
+ checkApStateChangedBroadcast(capturedIntents.get(0), WIFI_AP_STATE_ENABLING,
+ WIFI_AP_STATE_DISABLED, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode);
+ checkApStateChangedBroadcast(capturedIntents.get(1), WIFI_AP_STATE_ENABLED,
+ WIFI_AP_STATE_ENABLING, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode);
+ checkApStateChangedBroadcast(capturedIntents.get(2), WIFI_AP_STATE_DISABLING,
+ WIFI_AP_STATE_ENABLED, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode);
+ checkApStateChangedBroadcast(capturedIntents.get(3), WIFI_AP_STATE_DISABLED,
+ WIFI_AP_STATE_DISABLING, HOTSPOT_NO_ERROR, WIFI_IFACE_NAME, mode);
+ }
+
@Test
- public void loadComponentsFailure() throws Exception {
- when(mWifiNative.loadDriver()).thenReturn(false);
- when(mWifiNative.startHal()).thenReturn(false);
- when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(false);
+ public void loadComponentsInApModeForTethering() throws Exception {
+ loadComponentsInApMode(WifiManager.IFACE_IP_MODE_TETHERED);
+ }
+ @Test
+ public void loadComponentsInApModeForLOHS() throws Exception {
+ loadComponentsInApMode(WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
+ }
+
+ @Test
+ public void shouldRequireSupplicantStartupToLeaveInitialState() throws Exception {
+ when(mWifiNative.enableSupplicant()).thenReturn(false);
+ mWsm.setSupplicantRunning(true);
+ mLooper.dispatchAll();
+ assertEquals("InitialState", getCurrentState().getName());
+ // we should not be sending a wifi enabling update
+ verify(mContext, never()).sendStickyBroadcastAsUser(
+ (Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
+ }
+
+ @Test
+ public void shouldRequireWificondToLeaveInitialState() throws Exception {
+ // We start out with valid default values, break them going backwards so that
+ // we test all the bailout cases.
+
+ // ClientInterface dies after creation.
+ doThrow(new RemoteException()).when(mClientInterfaceBinder).linkToDeath(any(), anyInt());
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
- when(mWifiNative.loadDriver()).thenReturn(true);
+ // Failed to even create the client interface.
+ when(mWificond.createClientInterface()).thenReturn(null);
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
- when(mWifiNative.startHal()).thenReturn(true);
+ // Failed to get wificond proxy.
+ when(mWifiInjector.makeWificond()).thenReturn(null);
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
}
- /**
- * Test to check that mode changes from WifiController will be properly handled in the
- * InitialState by WifiStateMachine.
- */
@Test
- public void checkOperationalModeInInitialState() throws Exception {
- when(mWifiNative.loadDriver()).thenReturn(true);
- when(mWifiNative.startHal()).thenReturn(true);
- when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(true);
+ public void loadComponentsFailure() throws Exception {
+ when(mWifiNative.enableSupplicant()).thenReturn(false);
+ mWsm.setSupplicantRunning(true);
+ mLooper.dispatchAll();
+ assertEquals("InitialState", getCurrentState().getName());
+
+ mWsm.setSupplicantRunning(true);
+ mLooper.dispatchAll();
+ assertEquals("InitialState", getCurrentState().getName());
+ }
+
+ @Test
+ public void checkInitialStateStickyWhenDisabledMode() throws Exception {
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ mWsm.setOperationalMode(WifiStateMachine.DISABLED_MODE);
+ mLooper.dispatchAll();
+ assertEquals(WifiStateMachine.DISABLED_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("InitialState", getCurrentState().getName());
+ }
+
+ @Test
+ public void shouldStartSupplicantWhenConnectModeRequested() throws Exception {
+ // The first time we start out in InitialState, we sit around here.
+ mLooper.dispatchAll();
+ assertEquals("InitialState", getCurrentState().getName());
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+
+ // But if someone tells us to enter connect mode, we start up supplicant
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+ assertEquals("SupplicantStartingState", getCurrentState().getName());
+ }
+
+ /**
+ * Test that mode changes accurately reflect the value for isWifiEnabled.
+ */
+ @Test
+ public void checkIsWifiEnabledForModeChanges() throws Exception {
+ // Check initial state
+ mLooper.dispatchAll();
+ assertEquals("InitialState", getCurrentState().getName());
+ assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
+
+ mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
+ startSupplicantAndDispatchMessages();
+ mWsm.setSupplicantRunning(true);
+ mLooper.dispatchAll();
+ assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("ScanModeState", getCurrentState().getName());
+ assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
+ verify(mContext, never()).sendStickyBroadcastAsUser(
+ (Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
+
+
+ // switch to connect mode and verify wifi is reported as enabled
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals(WifiManager.WIFI_STATE_ENABLED, mWsm.syncGetWifiState());
+ verify(mContext).sendStickyBroadcastAsUser(
+ (Intent) argThat(new WifiEnablingStateIntentMatcher()), eq(UserHandle.ALL));
+
+ // reset the expectations on mContext since we did get an expected broadcast, but we should
+ // not on the next transition
+ reset(mContext);
+
+ // now go back to scan mode with "wifi disabled" to verify the reported wifi state.
mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE);
mLooper.dispatchAll();
assertEquals(WifiStateMachine.SCAN_ONLY_WITH_WIFI_OFF_MODE,
- mWsm.getOperationalModeForTest());
+ mWsm.getOperationalModeForTest());
+ assertEquals("ScanModeState", getCurrentState().getName());
+ assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
+ verify(mContext, never()).sendStickyBroadcastAsUser(
+ (Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
- mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
+ // now go to AP mode
+ mWsm.setSupplicantRunning(false);
+ mWsm.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP);
+ mWsm.sendMessage(WifiMonitor.SUP_DISCONNECTION_EVENT);
+ SoftApModeConfiguration config = new SoftApModeConfiguration(
+ WifiManager.IFACE_IP_MODE_TETHERED, new WifiConfiguration());
+ mWsm.setHostApRunning(config, true);
mLooper.dispatchAll();
- assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("SoftApState", getCurrentState().getName());
+ assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
+ verify(mContext, never()).sendStickyBroadcastAsUser(
+ (Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
+ }
- mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
- mLooper.dispatchAll();
- assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ private class WifiEnablingStateIntentMatcher implements ArgumentMatcher<Intent> {
+ @Override
+ public boolean matches(Intent intent) {
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION != intent.getAction()) {
+ // not the correct type
+ return false;
+ }
+ return WifiManager.WIFI_STATE_ENABLING
+ == intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_DISABLED);
+ }
}
/**
@@ -519,10 +705,6 @@
*/
@Test
public void checkStartInCorrectStateAfterChangingInitialState() throws Exception {
- when(mWifiNative.loadDriver()).thenReturn(true);
- when(mWifiNative.startHal()).thenReturn(true);
- when(mWifiNative.startSupplicant(anyBoolean())).thenReturn(true);
-
// Check initial state
mLooper.dispatchAll();
assertEquals("InitialState", getCurrentState().getName());
@@ -534,10 +716,74 @@
assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
// Start supplicant so we move to the next state
+ startSupplicantAndDispatchMessages();
+
+ assertEquals("ScanModeState", getCurrentState().getName());
+ verify(mContext, never()).sendStickyBroadcastAsUser(
+ (Intent) argThat(new WifiEnablingStateIntentMatcher()), any());
+ }
+
+ /**
+ * Verifies that configs can be removed when in client mode.
+ */
+ @Test
+ public void canRemoveNetworkConfigInClientMode() throws Exception {
+ boolean result;
+ when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
+ initializeAndAddNetworkAndVerifySuccess();
+ mLooper.startAutoDispatch();
+ result = mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0);
+ mLooper.stopAutoDispatch();
+ assertTrue(result);
+ }
+
+ /**
+ * Verifies that configs can be removed when not in client mode.
+ */
+ @Test
+ public void canRemoveNetworkConfigWhenWifiDisabled() {
+ boolean result;
+ when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
+ mLooper.startAutoDispatch();
+ result = mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0);
+ mLooper.stopAutoDispatch();
+
+ assertTrue(result);
+ verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
+ }
+
+ /**
+ * Verifies that configs can be forgotten when in client mode.
+ */
+ @Test
+ public void canForgetNetworkConfigInClientMode() throws Exception {
+ when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
+ initializeAndAddNetworkAndVerifySuccess();
+ mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID);
+ mLooper.dispatchAll();
+ verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
+ }
+
+ /**
+ * Verifies that configs can be removed when not in client mode.
+ */
+ @Test
+ public void canForgetNetworkConfigWhenWifiDisabled() throws Exception {
+ when(mWifiConfigManager.removeNetwork(eq(0), anyInt())).thenReturn(true);
+ mWsm.sendMessage(WifiManager.FORGET_NETWORK, 0, MANAGED_PROFILE_UID);
+ mLooper.dispatchAll();
+ verify(mWifiConfigManager).removeNetwork(anyInt(), anyInt());
+ }
+
+ /**
+ * Helper method to move through SupplicantStarting and SupplicantStarted states.
+ */
+ private void startSupplicantAndDispatchMessages() throws Exception {
mWsm.setSupplicantRunning(true);
mLooper.dispatchAll();
+
assertEquals("SupplicantStartingState", getCurrentState().getName());
- when(mWifiNative.setBand(anyInt())).thenReturn(true);
+
when(mWifiNative.setDeviceName(anyString())).thenReturn(true);
when(mWifiNative.setManufacturer(anyString())).thenReturn(true);
when(mWifiNative.setModelName(anyString())).thenReturn(true);
@@ -550,77 +796,24 @@
mWsm.sendMessage(WifiMonitor.SUP_CONNECTION_EVENT);
mLooper.dispatchAll();
-
- assertEquals("ScanModeState", getCurrentState().getName());
- }
-
- private void addNetworkAndVerifySuccess() throws Exception {
- addNetworkAndVerifySuccess(false);
}
private void addNetworkAndVerifySuccess(boolean isHidden) throws Exception {
- loadComponents();
-
- final HashMap<String, String> nameToValue = new HashMap<String, String>();
-
- when(mWifiNative.addNetwork()).thenReturn(0);
- when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString()))
- .then(new AnswerWithArguments() {
- public boolean answer(int netId, String name, String value) {
- if (netId != 0) {
- Log.d(TAG, "Can't set var " + name + " for " + netId);
- return false;
- }
-
- Log.d(TAG, "Setting var " + name + " to " + value + " for " + netId);
- nameToValue.put(name, value);
- return true;
- }
- });
-
- when(mWifiNative.setNetworkExtra(anyInt(), anyString(), (Map<String, String>) anyObject()))
- .then(new AnswerWithArguments() {
- public boolean answer(int netId, String name, Map<String, String> values) {
- if (netId != 0) {
- Log.d(TAG, "Can't set extra " + name + " for " + netId);
- return false;
- }
-
- Log.d(TAG, "Setting extra for " + netId);
- return true;
- }
- });
-
- when(mWifiNative.getNetworkVariable(anyInt(), anyString()))
- .then(new AnswerWithArguments() {
- public String answer(int netId, String name) throws Throwable {
- if (netId != 0) {
- Log.d(TAG, "Can't find var " + name + " for " + netId);
- return null;
- }
- String value = nameToValue.get(name);
- if (value != null) {
- Log.d(TAG, "Returning var " + name + " to " + value + " for " + netId);
- } else {
- Log.d(TAG, "Can't find var " + name + " for " + netId);
- }
- return value;
- }
- });
-
WifiConfiguration config = new WifiConfiguration();
config.SSID = sSSID;
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.hiddenSSID = isHidden;
+
+ when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+ .thenReturn(new NetworkUpdateResult(0));
+ when(mWifiConfigManager.getSavedNetworks()).thenReturn(Arrays.asList(config));
+ when(mWifiConfigManager.getConfiguredNetwork(0)).thenReturn(config);
+
mLooper.startAutoDispatch();
mWsm.syncAddOrUpdateNetwork(mWsmAsyncChannel, config);
mLooper.stopAutoDispatch();
- verify(mWifiNative).addNetwork();
- verify(mWifiNative).setNetworkVariable(0, "ssid", sHexSSID);
- if (isHidden) {
- verify(mWifiNative).setNetworkVariable(0, "scan_ssid", Integer.toString(1));
- }
+ verify(mWifiConfigManager).addOrUpdateNetwork(eq(config), anyInt());
mLooper.startAutoDispatch();
List<WifiConfiguration> configs = mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel);
@@ -632,175 +825,13 @@
assertTrue(config2.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE));
}
- private void addNetworkAndVerifyFailure() throws Exception {
- loadComponents();
-
- final WifiConfiguration config = new WifiConfiguration();
- config.SSID = sSSID;
- config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
-
- mLooper.startAutoDispatch();
- mWsm.syncAddOrUpdateNetwork(mWsmAsyncChannel, config);
- mLooper.stopAutoDispatch();
-
- verify(mWifiNative, never()).addNetwork();
- verify(mWifiNative, never()).setNetworkVariable(anyInt(), anyString(), anyString());
-
- mLooper.startAutoDispatch();
- assertTrue(mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel).isEmpty());
- mLooper.stopAutoDispatch();
+ private void initializeAndAddNetworkAndVerifySuccess() throws Exception {
+ initializeAndAddNetworkAndVerifySuccess(false);
}
- /**
- * Verifies that the current foreground user is allowed to add a network.
- */
- @Test
- public void addNetworkAsCurrentUser() throws Exception {
- addNetworkAndVerifySuccess();
- }
-
- /**
- * Verifies that a managed profile of the current foreground user is allowed to add a network.
- */
- @Test
- public void addNetworkAsCurrentUsersManagedProfile() throws Exception {
- BinderUtil.setUid(MANAGED_PROFILE_UID);
- addNetworkAndVerifySuccess();
- }
-
- /**
- * Verifies that a background user is not allowed to add a network.
- */
- @Test
- public void addNetworkAsOtherUser() throws Exception {
- BinderUtil.setUid(OTHER_USER_UID);
- addNetworkAndVerifyFailure();
- }
-
- private void removeNetworkAndVerifySuccess() throws Exception {
- when(mWifiNative.removeNetwork(0)).thenReturn(true);
- mLooper.startAutoDispatch();
- assertTrue(mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0));
- mLooper.stopAutoDispatch();
-
- mLooper.startAutoDispatch();
- assertTrue(mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel).isEmpty());
- mLooper.stopAutoDispatch();
- }
-
- private void removeNetworkAndVerifyFailure() throws Exception {
- mLooper.startAutoDispatch();
- assertFalse(mWsm.syncRemoveNetwork(mWsmAsyncChannel, 0));
- mLooper.stopAutoDispatch();
-
- mLooper.startAutoDispatch();
- assertEquals(1, mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel).size());
- mLooper.stopAutoDispatch();
- verify(mWifiNative, never()).removeNetwork(anyInt());
- }
-
- /**
- * Verifies that the current foreground user is allowed to remove a network.
- */
- @Test
- public void removeNetworkAsCurrentUser() throws Exception {
- addNetworkAndVerifySuccess();
- removeNetworkAndVerifySuccess();
- }
-
- /**
- * Verifies that a managed profile of the current foreground user is allowed to remove a
- * network.
- */
- @Test
- public void removeNetworkAsCurrentUsersManagedProfile() throws Exception {
- addNetworkAndVerifySuccess();
- BinderUtil.setUid(MANAGED_PROFILE_UID);
- removeNetworkAndVerifySuccess();
- }
-
- /**
- * Verifies that a background user is not allowed to remove a network.
- */
- @Test
- public void removeNetworkAsOtherUser() throws Exception {
- addNetworkAndVerifySuccess();
- BinderUtil.setUid(OTHER_USER_UID);
- removeNetworkAndVerifyFailure();
- }
-
- private void enableNetworkAndVerifySuccess() throws Exception {
- when(mWifiNative.selectNetwork(0)).thenReturn(true);
-
- mLooper.startAutoDispatch();
- assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true));
- mLooper.stopAutoDispatch();
-
- verify(mWifiNative).selectNetwork(0);
- }
-
- private void enableNetworkAndVerifyFailure() throws Exception {
- mLooper.startAutoDispatch();
- assertFalse(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true));
- mLooper.stopAutoDispatch();
-
- verify(mWifiNative, never()).selectNetwork(anyInt());
- }
-
- /**
- * Verifies that the current foreground user is allowed to enable a network.
- */
- @Test
- public void enableNetworkAsCurrentUser() throws Exception {
- addNetworkAndVerifySuccess();
- enableNetworkAndVerifySuccess();
- }
-
- /**
- * Verifies that a managed profile of the current foreground user is allowed to enable a
- * network.
- */
- @Test
- public void enableNetworkAsCurrentUsersManagedProfile() throws Exception {
- addNetworkAndVerifySuccess();
- BinderUtil.setUid(MANAGED_PROFILE_UID);
- enableNetworkAndVerifySuccess();
- }
-
- /**
- * Verifies that a background user is not allowed to enable a network.
- */
- @Test
- public void enableNetworkAsOtherUser() throws Exception {
- addNetworkAndVerifySuccess();
- BinderUtil.setUid(OTHER_USER_UID);
- enableNetworkAndVerifyFailure();
- }
-
- private void forgetNetworkAndVerifySuccess() throws Exception {
- when(mWifiNative.removeNetwork(0)).thenReturn(true);
- mLooper.startAutoDispatch();
- final Message result =
- mWsmAsyncChannel.sendMessageSynchronously(WifiManager.FORGET_NETWORK, 0);
- mLooper.stopAutoDispatch();
- assertEquals(WifiManager.FORGET_NETWORK_SUCCEEDED, result.what);
- result.recycle();
- mLooper.startAutoDispatch();
- assertTrue(mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel).isEmpty());
- mLooper.stopAutoDispatch();
- }
-
- private void forgetNetworkAndVerifyFailure() throws Exception {
- mLooper.startAutoDispatch();
- final Message result =
- mWsmAsyncChannel.sendMessageSynchronously(WifiManager.FORGET_NETWORK, 0);
- mLooper.stopAutoDispatch();
- assertEquals(WifiManager.FORGET_NETWORK_FAILED, result.what);
- result.recycle();
- mLooper.startAutoDispatch();
- assertEquals(1, mWsm.syncGetConfiguredNetworks(-1, mWsmAsyncChannel).size());
- mLooper.stopAutoDispatch();
- verify(mWifiNative, never()).removeNetwork(anyInt());
+ private void initializeAndAddNetworkAndVerifySuccess(boolean isHidden) throws Exception {
+ loadComponentsInStaMode();
+ addNetworkAndVerifySuccess(isHidden);
}
/**
@@ -821,37 +852,7 @@
return null;
}
- /**
- * Verifies that the current foreground user is allowed to forget a network.
- */
- @Test
- public void forgetNetworkAsCurrentUser() throws Exception {
- addNetworkAndVerifySuccess();
- forgetNetworkAndVerifySuccess();
- }
-
- /**
- * Verifies that a managed profile of the current foreground user is allowed to forget a
- * network.
- */
- @Test
- public void forgetNetworkAsCurrentUsersManagedProfile() throws Exception {
- addNetworkAndVerifySuccess();
- BinderUtil.setUid(MANAGED_PROFILE_UID);
- forgetNetworkAndVerifySuccess();
- }
-
- /**
- * Verifies that a background user is not allowed to forget a network.
- */
- @Test
- public void forgetNetworkAsOtherUser() throws Exception {
- addNetworkAndVerifySuccess();
- BinderUtil.setUid(OTHER_USER_UID);
- forgetNetworkAndVerifyFailure();
- }
-
- private void verifyScan(int band, int reportEvents, Set<Integer> configuredNetworkIds) {
+ private void verifyScan(int band, int reportEvents, Set<String> hiddenNetworkSSIDSet) {
ArgumentCaptor<WifiScanner.ScanSettings> scanSettingsCaptor =
ArgumentCaptor.forClass(WifiScanner.ScanSettings.class);
ArgumentCaptor<WifiScanner.ScanListener> scanListenerCaptor =
@@ -862,16 +863,16 @@
assertEquals("band", band, actualSettings.band);
assertEquals("reportEvents", reportEvents, actualSettings.reportEvents);
- if (configuredNetworkIds == null) {
- configuredNetworkIds = new HashSet<>();
+ if (hiddenNetworkSSIDSet == null) {
+ hiddenNetworkSSIDSet = new HashSet<>();
}
- Set<Integer> actualConfiguredNetworkIds = new HashSet<>();
- if (actualSettings.hiddenNetworkIds != null) {
- for (int i = 0; i < actualSettings.hiddenNetworkIds.length; ++i) {
- actualConfiguredNetworkIds.add(actualSettings.hiddenNetworkIds[i]);
+ Set<String> actualHiddenNetworkSSIDSet = new HashSet<>();
+ if (actualSettings.hiddenNetworks != null) {
+ for (int i = 0; i < actualSettings.hiddenNetworks.length; ++i) {
+ actualHiddenNetworkSSIDSet.add(actualSettings.hiddenNetworks[i].ssid);
}
}
- assertEquals("configured networks", configuredNetworkIds, actualConfiguredNetworkIds);
+ assertEquals("hidden networks", hiddenNetworkSSIDSet, actualHiddenNetworkSSIDSet);
when(mWifiNative.getScanResults()).thenReturn(getMockScanResults());
mWsm.sendMessage(WifiMonitor.SCAN_RESULTS_EVENT);
@@ -884,7 +885,7 @@
@Test
public void scan() throws Exception {
- addNetworkAndVerifySuccess();
+ initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mWsm.startScan(-1, 0, null, null);
@@ -897,7 +898,13 @@
@Test
public void scanWithHiddenNetwork() throws Exception {
- addNetworkAndVerifySuccess(true);
+ initializeAndAddNetworkAndVerifySuccess(true);
+
+ Set<String> hiddenNetworkSet = new HashSet<>();
+ hiddenNetworkSet.add(sSSID);
+ List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworkList = new ArrayList<>();
+ hiddenNetworkList.add(new WifiScanner.ScanSettings.HiddenNetwork(sSSID));
+ when(mWifiConfigManager.retrieveHiddenNetworkList()).thenReturn(hiddenNetworkList);
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mWsm.startScan(-1, 0, null, null);
@@ -906,21 +913,25 @@
verifyScan(WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN
| WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT,
- mWifiConfigManager.getHiddenConfiguredNetworkIds());
+ hiddenNetworkSet);
}
@Test
public void connect() throws Exception {
- addNetworkAndVerifySuccess();
+ initializeAndAddNetworkAndVerifySuccess();
+ when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt())).thenReturn(true);
+ when(mWifiConfigManager.checkAndUpdateLastConnectUid(eq(0), anyInt())).thenReturn(true);
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
+ verify(mWifiNative).removeAllNetworks();
mLooper.startAutoDispatch();
- mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
+ assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true));
mLooper.stopAutoDispatch();
- verify(mWifiNative).selectNetwork(0);
+ verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
+ verify(mWifiConnectivityManager).setUserConnectChoice(eq(0));
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
@@ -944,8 +955,101 @@
}
@Test
+ public void connectWithNoEnablePermission() throws Exception {
+ initializeAndAddNetworkAndVerifySuccess();
+ when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt())).thenReturn(false);
+ when(mWifiConfigManager.checkAndUpdateLastConnectUid(eq(0), anyInt())).thenReturn(false);
+
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+ verify(mWifiNative).removeAllNetworks();
+
+ mLooper.startAutoDispatch();
+ assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true));
+ mLooper.stopAutoDispatch();
+
+ verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
+ verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(0));
+
+ mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
+ mLooper.dispatchAll();
+
+ mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+ new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
+ mLooper.dispatchAll();
+
+ assertEquals("ObtainingIpState", getCurrentState().getName());
+
+ DhcpResults dhcpResults = new DhcpResults();
+ dhcpResults.setGateway("1.2.3.4");
+ dhcpResults.setIpAddress("192.168.1.100", 0);
+ dhcpResults.addDns("8.8.8.8");
+ dhcpResults.setLeaseDuration(3600);
+
+ mTestIpManager.injectDhcpSuccess(dhcpResults);
+ mLooper.dispatchAll();
+
+ assertEquals("ConnectedState", getCurrentState().getName());
+ }
+
+ @Test
+ public void enableWithInvalidNetworkId() throws Exception {
+ initializeAndAddNetworkAndVerifySuccess();
+ when(mWifiConfigManager.getConfiguredNetwork(eq(0))).thenReturn(null);
+
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+ verify(mWifiNative).removeAllNetworks();
+
+ mLooper.startAutoDispatch();
+ assertFalse(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true));
+ mLooper.stopAutoDispatch();
+
+ verify(mWifiConfigManager, never()).enableNetwork(eq(0), eq(true), anyInt());
+ verify(mWifiConfigManager, never()).checkAndUpdateLastConnectUid(eq(0), anyInt());
+ }
+
+ /**
+ * If caller tries to connect to a network that is already connected, the connection request
+ * should succeed.
+ *
+ * Test: Create and connect to a network, then try to reconnect to the same network. Verify
+ * that connection request returns with CONNECT_NETWORK_SUCCEEDED.
+ */
+ @Test
+ public void reconnectToConnectedNetwork() throws Exception {
+ initializeAndAddNetworkAndVerifySuccess();
+
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+ verify(mWifiNative).removeAllNetworks();
+
+ mLooper.startAutoDispatch();
+ mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
+ mLooper.stopAutoDispatch();
+
+ verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
+
+ mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
+ mLooper.dispatchAll();
+
+ mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+ new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
+ mLooper.dispatchAll();
+
+ assertEquals("ObtainingIpState", getCurrentState().getName());
+
+ // try to reconnect
+ mLooper.startAutoDispatch();
+ Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.CONNECT_NETWORK, 0);
+ mLooper.stopAutoDispatch();
+
+ assertEquals(WifiManager.CONNECT_NETWORK_SUCCEEDED, reply.what);
+ }
+
+ @Test
public void testDhcpFailure() throws Exception {
- addNetworkAndVerifySuccess();
+ initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
@@ -954,7 +1058,7 @@
mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
mLooper.stopAutoDispatch();
- verify(mWifiNative).selectNetwork(0);
+ verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
@@ -973,7 +1077,7 @@
@Test
public void testBadNetworkEvent() throws Exception {
- addNetworkAndVerifySuccess();
+ initializeAndAddNetworkAndVerifySuccess();
mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
mLooper.dispatchAll();
@@ -982,7 +1086,7 @@
mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true);
mLooper.stopAutoDispatch();
- verify(mWifiNative).selectNetwork(0);
+ verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
mWsm.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, 0, 0, sBSSID);
mLooper.dispatchAll();
@@ -1019,21 +1123,6 @@
}
/**
- * WifiConfigurations default to HasEverConnected to false, creating and adding a config should
- * not update this value to true.
- *
- * Test: Successfully add a network. Check the config and verify
- * WifiConfiguration.getHasEverConnected() is false.
- */
- @Test
- public void addNetworkDoesNotSetHasEverConnectedTrue() throws Exception {
- addNetworkAndVerifySuccess();
-
- WifiConfiguration checkConfig = getWifiConfigurationForNetwork(DEFAULT_TEST_SSID);
- assertFalse(checkConfig.getNetworkSelectionStatus().getHasEverConnected());
- }
-
- /**
* Successfully connecting to a network will set WifiConfiguration's value of HasEverConnected
* to true.
*
@@ -1043,9 +1132,7 @@
@Test
public void setHasEverConnectedTrueOnConnect() throws Exception {
connect();
-
- WifiConfiguration checkConfig = getWifiConfigurationForNetwork(DEFAULT_TEST_SSID);
- assertTrue(checkConfig.getNetworkSelectionStatus().getHasEverConnected());
+ verify(mWifiConfigManager, atLeastOnce()).updateNetworkAfterConnect(0);
}
/**
@@ -1057,91 +1144,17 @@
@Test
public void connectionFailureDoesNotSetHasEverConnectedTrue() throws Exception {
testDhcpFailure();
-
- WifiConfiguration checkConfig = getWifiConfigurationForNetwork(DEFAULT_TEST_SSID);
- assertFalse(checkConfig.getNetworkSelectionStatus().getHasEverConnected());
- }
-
- @Test
- public void handleUserSwitch() throws Exception {
- assertEquals(UserHandle.USER_SYSTEM, mWifiConfigManager.getCurrentUserId());
-
- mWsm.handleUserSwitch(10);
- mLooper.dispatchAll();
-
- assertEquals(10, mWifiConfigManager.getCurrentUserId());
+ verify(mWifiConfigManager, never()).updateNetworkAfterConnect(0);
}
@Test
public void iconQueryTest() throws Exception {
- /* enable wi-fi */
- addNetworkAndVerifySuccess();
-
- long bssid = 0x1234567800FFL;
- String filename = "iconFileName.png";
- String command = "REQ_HS20_ICON " + Utils.macToString(bssid) + " " + filename;
-
- when(mWifiNative.doCustomSupplicantCommand(command)).thenReturn("OK");
-
- mLooper.startAutoDispatch();
- boolean result = mWsm.syncQueryPasspointIcon(mWsmAsyncChannel, bssid, filename);
- mLooper.stopAutoDispatch();
-
- verify(mWifiNative).doCustomSupplicantCommand(command);
- assertEquals(true, result);
+ // TODO(b/31065385): Passpoint config management.
}
- private String createSimChallengeRequest(byte[] challengeValue) {
- // Produce a base64 encoded length byte + data.
- byte[] challengeLengthAndValue = new byte[challengeValue.length + 1];
- challengeLengthAndValue[0] = (byte) challengeValue.length;
- for (int i = 0; i < challengeValue.length; ++i) {
- challengeLengthAndValue[i + 1] = challengeValue[i];
- }
- return Base64.encodeToString(challengeLengthAndValue, android.util.Base64.NO_WRAP);
- }
-
- private String createSimAuthResponse(byte[] sresValue, byte[] kcValue) {
- // Produce a base64 encoded sres length byte + sres + kc length byte + kc.
- int overallLength = sresValue.length + kcValue.length + 2;
- byte[] result = new byte[sresValue.length + kcValue.length + 2];
- int idx = 0;
- result[idx++] = (byte) sresValue.length;
- for (int i = 0; i < sresValue.length; ++i) {
- result[idx++] = sresValue[i];
- }
- result[idx++] = (byte) kcValue.length;
- for (int i = 0; i < kcValue.length; ++i) {
- result[idx++] = kcValue[i];
- }
- return Base64.encodeToString(result, Base64.NO_WRAP);
- }
-
- /** Verifies function getGsmSimAuthResponse method. */
@Test
- public void getGsmSimAuthResponseTest() throws Exception {
- TelephonyManager tm = mock(TelephonyManager.class);
- final String[] invalidRequests = { null, "", "XXXX" };
- assertEquals("", mWsm.getGsmSimAuthResponse(invalidRequests, tm));
-
- final String[] failedRequests = { "5E5F" };
- when(tm.getIccAuthentication(anyInt(), anyInt(),
- eq(createSimChallengeRequest(new byte[] { 0x5e, 0x5f })))).thenReturn(null);
- assertEquals(null, mWsm.getGsmSimAuthResponse(failedRequests, tm));
-
- when(tm.getIccAuthentication(2, tm.AUTHTYPE_EAP_SIM,
- createSimChallengeRequest(new byte[] { 0x1a, 0x2b })))
- .thenReturn(null);
- when(tm.getIccAuthentication(1, tm.AUTHTYPE_EAP_SIM,
- createSimChallengeRequest(new byte[] { 0x1a, 0x2b })))
- .thenReturn(createSimAuthResponse(new byte[] { 0x1D, 0x2C },
- new byte[] { 0x3B, 0x4A }));
- when(tm.getIccAuthentication(1, tm.AUTHTYPE_EAP_SIM,
- createSimChallengeRequest(new byte[] { 0x01, 0x23 })))
- .thenReturn(createSimAuthResponse(new byte[] { 0x33, 0x22 },
- new byte[] { 0x11, 0x00 }));
- assertEquals(":3b4a:1d2c:1100:3322", mWsm.getGsmSimAuthResponse(
- new String[] { "1A2B", "0123" }, tm));
+ public void verboseLogRecSizeIsGreaterThanNormalSize() {
+ assertTrue(LOG_REC_LIMIT_IN_VERBOSE_MODE > WifiStateMachine.NUM_LOG_RECS_NORMAL);
}
/**
@@ -1149,47 +1162,66 @@
*/
@Test
public void normalLogRecSizeIsUsedByDefault() {
- for (int i = 0; i < WifiStateMachine.NUM_LOG_RECS_NORMAL * 2; i++) {
- mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
- }
- mLooper.dispatchAll();
- assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecSize());
+ assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecMaxSize());
}
/**
* Verifies that, in verbose mode, we allow a larger number of log records.
*/
@Test
- public void enablingVerboseLoggingIncreasesLogRecSize() {
- assertTrue(LOG_REC_LIMIT_IN_VERBOSE_MODE > WifiStateMachine.NUM_LOG_RECS_NORMAL);
+ public void enablingVerboseLoggingUpdatesLogRecSize() {
mWsm.enableVerboseLogging(1);
- for (int i = 0; i < LOG_REC_LIMIT_IN_VERBOSE_MODE * 2; i++) {
- mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
- }
- mLooper.dispatchAll();
- assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mWsm.getLogRecSize());
+ assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mWsm.getLogRecMaxSize());
}
- /**
- * Verifies that moving from verbose mode to normal mode resets the buffer, and limits new
- * records to a small number of entries.
- */
@Test
- public void disablingVerboseLoggingClearsRecordsAndDecreasesLogRecSize() {
- mWsm.enableVerboseLogging(1);
- for (int i = 0; i < LOG_REC_LIMIT_IN_VERBOSE_MODE; i++) {
- mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
- }
+ public void disablingVerboseLoggingClearsRecords() {
+ mWsm.sendMessage(WifiStateMachine.CMD_DISCONNECT);
mLooper.dispatchAll();
- assertEquals(LOG_REC_LIMIT_IN_VERBOSE_MODE, mWsm.getLogRecSize());
+ assertTrue(mWsm.getLogRecSize() >= 1);
mWsm.enableVerboseLogging(0);
assertEquals(0, mWsm.getLogRecSize());
- for (int i = 0; i < LOG_REC_LIMIT_IN_VERBOSE_MODE; i++) {
- mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
- }
+ }
+
+ @Test
+ public void disablingVerboseLoggingUpdatesLogRecSize() {
+ mWsm.enableVerboseLogging(1);
+ mWsm.enableVerboseLogging(0);
+ assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecMaxSize());
+ }
+
+ @Test
+ public void logRecsIncludeDisconnectCommand() {
+ // There's nothing special about the DISCONNECT command. It's just representative of
+ // "normal" commands.
+ mWsm.sendMessage(WifiStateMachine.CMD_DISCONNECT);
mLooper.dispatchAll();
- assertEquals(WifiStateMachine.NUM_LOG_RECS_NORMAL, mWsm.getLogRecSize());
+ assertEquals(1, mWsm.copyLogRecs()
+ .stream()
+ .filter(logRec -> logRec.getWhat() == WifiStateMachine.CMD_DISCONNECT)
+ .count());
+ }
+
+ @Test
+ public void logRecsExcludeRssiPollCommandByDefault() {
+ mWsm.sendMessage(WifiStateMachine.CMD_RSSI_POLL);
+ mLooper.dispatchAll();
+ assertEquals(0, mWsm.copyLogRecs()
+ .stream()
+ .filter(logRec -> logRec.getWhat() == WifiStateMachine.CMD_RSSI_POLL)
+ .count());
+ }
+
+ @Test
+ public void logRecsIncludeRssiPollCommandWhenVerboseLoggingIsEnabled() {
+ mWsm.enableVerboseLogging(1);
+ mWsm.sendMessage(WifiStateMachine.CMD_RSSI_POLL);
+ mLooper.dispatchAll();
+ assertEquals(1, mWsm.copyLogRecs()
+ .stream()
+ .filter(logRec -> logRec.getWhat() == WifiStateMachine.CMD_RSSI_POLL)
+ .count());
}
/** Verifies that enabling verbose logging sets the hal log property in eng builds. */
@@ -1240,17 +1272,17 @@
/** Verifies that syncGetSupportedFeatures() masks out capabilities based on system flags. */
@Test
public void syncGetSupportedFeatures() {
- final int featureNan = WifiManager.WIFI_FEATURE_NAN;
+ final int featureAware = WifiManager.WIFI_FEATURE_AWARE;
final int featureInfra = WifiManager.WIFI_FEATURE_INFRA;
final int featureD2dRtt = WifiManager.WIFI_FEATURE_D2D_RTT;
final int featureD2apRtt = WifiManager.WIFI_FEATURE_D2AP_RTT;
assertEquals(0, testGetSupportedFeaturesCase(0, false));
assertEquals(0, testGetSupportedFeaturesCase(0, true));
- assertEquals(featureNan | featureInfra,
- testGetSupportedFeaturesCase(featureNan | featureInfra, false));
- assertEquals(featureNan | featureInfra,
- testGetSupportedFeaturesCase(featureNan | featureInfra, true));
+ assertEquals(featureAware | featureInfra,
+ testGetSupportedFeaturesCase(featureAware | featureInfra, false));
+ assertEquals(featureAware | featureInfra,
+ testGetSupportedFeaturesCase(featureAware | featureInfra, true));
assertEquals(featureInfra | featureD2dRtt,
testGetSupportedFeaturesCase(featureInfra | featureD2dRtt, false));
assertEquals(featureInfra,
@@ -1264,4 +1296,422 @@
assertEquals(featureInfra,
testGetSupportedFeaturesCase(featureInfra | featureD2dRtt | featureD2apRtt, true));
}
+
+ /**
+ * Verify that syncAddOrUpdatePasspointConfig will redirect calls to {@link PasspointManager}
+ * and returning the result that's returned from {@link PasspointManager}.
+ */
+ @Test
+ public void syncAddOrUpdatePasspointConfig() throws Exception {
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test.com");
+ config.setHomeSp(homeSp);
+
+ when(mPasspointManager.addOrUpdateProvider(config, MANAGED_PROFILE_UID)).thenReturn(true);
+ mLooper.startAutoDispatch();
+ assertTrue(mWsm.syncAddOrUpdatePasspointConfig(
+ mWsmAsyncChannel, config, MANAGED_PROFILE_UID));
+ mLooper.stopAutoDispatch();
+ reset(mPasspointManager);
+
+ when(mPasspointManager.addOrUpdateProvider(config, MANAGED_PROFILE_UID)).thenReturn(false);
+ mLooper.startAutoDispatch();
+ assertFalse(mWsm.syncAddOrUpdatePasspointConfig(
+ mWsmAsyncChannel, config, MANAGED_PROFILE_UID));
+ mLooper.stopAutoDispatch();
+ }
+
+ /**
+ * Verify that syncAddOrUpdatePasspointConfig will redirect calls to {@link PasspointManager}
+ * and returning the result that's returned from {@link PasspointManager} when in client mode.
+ */
+ @Test
+ public void syncAddOrUpdatePasspointConfigInClientMode() throws Exception {
+ loadComponentsInStaMode();
+ syncAddOrUpdatePasspointConfig();
+ }
+
+ /**
+ * Verify that syncRemovePasspointConfig will redirect calls to {@link PasspointManager}
+ * and returning the result that's returned from {@link PasspointManager}.
+ */
+ @Test
+ public void syncRemovePasspointConfig() throws Exception {
+ String fqdn = "test.com";
+ when(mPasspointManager.removeProvider(fqdn)).thenReturn(true);
+ mLooper.startAutoDispatch();
+ assertTrue(mWsm.syncRemovePasspointConfig(mWsmAsyncChannel, fqdn));
+ mLooper.stopAutoDispatch();
+ reset(mPasspointManager);
+
+ when(mPasspointManager.removeProvider(fqdn)).thenReturn(false);
+ mLooper.startAutoDispatch();
+ assertFalse(mWsm.syncRemovePasspointConfig(mWsmAsyncChannel, fqdn));
+ mLooper.stopAutoDispatch();
+ }
+
+ /**
+ * Verify that syncRemovePasspointConfig will redirect calls to {@link PasspointManager}
+ * and returning the result that's returned from {@link PasspointManager} when in client mode.
+ */
+ @Test
+ public void syncRemovePasspointConfigInClientMode() throws Exception {
+ loadComponentsInStaMode();
+ syncRemovePasspointConfig();
+ }
+
+ /**
+ * Verify that syncGetPasspointConfigs will redirect calls to {@link PasspointManager}
+ * and returning the result that's returned from {@link PasspointManager}.
+ */
+ @Test
+ public void syncGetPasspointConfigs() throws Exception {
+ // Setup expected configs.
+ List<PasspointConfiguration> expectedConfigs = new ArrayList<>();
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test.com");
+ config.setHomeSp(homeSp);
+ expectedConfigs.add(config);
+
+ when(mPasspointManager.getProviderConfigs()).thenReturn(expectedConfigs);
+ mLooper.startAutoDispatch();
+ assertEquals(expectedConfigs, mWsm.syncGetPasspointConfigs(mWsmAsyncChannel));
+ mLooper.stopAutoDispatch();
+ reset(mPasspointManager);
+
+ when(mPasspointManager.getProviderConfigs())
+ .thenReturn(new ArrayList<PasspointConfiguration>());
+ mLooper.startAutoDispatch();
+ assertTrue(mWsm.syncGetPasspointConfigs(mWsmAsyncChannel).isEmpty());
+ mLooper.stopAutoDispatch();
+ }
+
+ /**
+ * Verify that syncGetMatchingWifiConfig will redirect calls to {@link PasspointManager}
+ * with expected {@link WifiConfiguration} being returned when in client mode.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void syncGetMatchingWifiConfigInClientMode() throws Exception {
+ loadComponentsInStaMode();
+
+ when(mPasspointManager.getMatchingWifiConfig(any(ScanResult.class))).thenReturn(null);
+ mLooper.startAutoDispatch();
+ assertNull(mWsm.syncGetMatchingWifiConfig(new ScanResult(), mWsmAsyncChannel));
+ mLooper.stopAutoDispatch();
+ reset(mPasspointManager);
+
+ WifiConfiguration expectedConfig = new WifiConfiguration();
+ expectedConfig.SSID = "TestSSID";
+ when(mPasspointManager.getMatchingWifiConfig(any(ScanResult.class)))
+ .thenReturn(expectedConfig);
+ mLooper.startAutoDispatch();
+ WifiConfiguration actualConfig = mWsm.syncGetMatchingWifiConfig(new ScanResult(),
+ mWsmAsyncChannel);
+ mLooper.stopAutoDispatch();
+ assertEquals(expectedConfig.SSID, actualConfig.SSID);
+ }
+
+ /**
+ * Verify that syncGetMatchingWifiConfig will be a no-op and return {@code null} when not in
+ * client mode.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void syncGetMatchingWifiConfigInNonClientMode() throws Exception {
+ mLooper.startAutoDispatch();
+ assertNull(mWsm.syncGetMatchingWifiConfig(new ScanResult(), mWsmAsyncChannel));
+ mLooper.stopAutoDispatch();
+ verify(mPasspointManager, never()).getMatchingWifiConfig(any(ScanResult.class));
+ }
+
+ /**
+ * Verify successful Wps PBC network connection.
+ */
+ @Test
+ public void wpsPbcConnectSuccess() throws Exception {
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+
+ when(mWifiNative.startWpsPbc(eq(sBSSID))).thenReturn(true);
+ WpsInfo wpsInfo = new WpsInfo();
+ wpsInfo.setup = WpsInfo.PBC;
+ wpsInfo.BSSID = sBSSID;
+
+ mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo);
+ mLooper.dispatchAll();
+ verify(mWifiNative).startWpsPbc(eq(sBSSID));
+
+ assertEquals("WpsRunningState", getCurrentState().getName());
+
+ setupMocksForWpsNetworkMigration();
+
+ mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null);
+ mLooper.dispatchAll();
+
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ verifyMocksForWpsNetworkMigration();
+ }
+
+ /**
+ * Verify failure in starting Wps PBC network connection.
+ */
+ @Test
+ public void wpsPbcConnectFailure() throws Exception {
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+
+ when(mWifiNative.startWpsPbc(eq(sBSSID))).thenReturn(false);
+ WpsInfo wpsInfo = new WpsInfo();
+ wpsInfo.setup = WpsInfo.PBC;
+ wpsInfo.BSSID = sBSSID;
+
+ mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo);
+ mLooper.dispatchAll();
+ verify(mWifiNative).startWpsPbc(eq(sBSSID));
+
+ assertFalse("WpsRunningState".equals(getCurrentState().getName()));
+ }
+
+ /**
+ * Verify successful Wps Pin Display network connection.
+ */
+ @Test
+ public void wpsPinDisplayConnectSuccess() throws Exception {
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+
+ when(mWifiNative.startWpsPinDisplay(eq(sBSSID))).thenReturn("34545434");
+ WpsInfo wpsInfo = new WpsInfo();
+ wpsInfo.setup = WpsInfo.DISPLAY;
+ wpsInfo.BSSID = sBSSID;
+
+ mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo);
+ mLooper.dispatchAll();
+ verify(mWifiNative).startWpsPinDisplay(eq(sBSSID));
+
+ assertEquals("WpsRunningState", getCurrentState().getName());
+
+ setupMocksForWpsNetworkMigration();
+
+ mWsm.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, null);
+ mLooper.dispatchAll();
+
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ verifyMocksForWpsNetworkMigration();
+ }
+
+ /**
+ * Verify failure in Wps Pin Display network connection.
+ */
+ @Test
+ public void wpsPinDisplayConnectFailure() throws Exception {
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+
+ when(mWifiNative.startWpsPinDisplay(eq(sBSSID))).thenReturn(null);
+ WpsInfo wpsInfo = new WpsInfo();
+ wpsInfo.setup = WpsInfo.DISPLAY;
+ wpsInfo.BSSID = sBSSID;
+
+ mWsm.sendMessage(WifiManager.START_WPS, 0, 0, wpsInfo);
+ mLooper.dispatchAll();
+ verify(mWifiNative).startWpsPinDisplay(eq(sBSSID));
+
+ assertFalse("WpsRunningState".equals(getCurrentState().getName()));
+ }
+
+ @Test
+ public void handleVendorHalDeath() throws Exception {
+ ArgumentCaptor<WifiNative.VendorHalDeathEventHandler> deathHandlerCapturer =
+ ArgumentCaptor.forClass(WifiNative.VendorHalDeathEventHandler.class);
+ when(mWifiNative.initializeVendorHal(deathHandlerCapturer.capture())).thenReturn(true);
+
+ // Trigger initialize to capture the death handler registration.
+ mLooper.startAutoDispatch();
+ assertTrue(mWsm.syncInitialize(mWsmAsyncChannel));
+ mLooper.stopAutoDispatch();
+
+ verify(mWifiNative).initializeVendorHal(any(WifiNative.VendorHalDeathEventHandler.class));
+ WifiNative.VendorHalDeathEventHandler deathHandler = deathHandlerCapturer.getValue();
+
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+
+ // Now trigger the death notification.
+ deathHandler.onDeath();
+ mLooper.dispatchAll();
+
+ verify(mWifiMetrics).incrementNumHalCrashes();
+ verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_HAL_CRASH));
+ }
+
+ @Test
+ public void handleWificondDeath() throws Exception {
+ ArgumentCaptor<StateMachineDeathRecipient> deathHandlerCapturer =
+ ArgumentCaptor.forClass(StateMachineDeathRecipient.class);
+
+ // Trigger initialize to capture the death handler registration.
+ loadComponentsInStaMode();
+
+ verify(mClientInterfaceBinder).linkToDeath(deathHandlerCapturer.capture(), anyInt());
+ StateMachineDeathRecipient deathHandler = deathHandlerCapturer.getValue();
+
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+
+ // Now trigger the death notification.
+ deathHandler.binderDied();
+ mLooper.dispatchAll();
+
+ verify(mWifiMetrics).incrementNumWificondCrashes();
+ verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_WIFICOND_CRASH));
+ }
+
+ private void setupMocksForWpsNetworkMigration() {
+ // Now trigger the network connection event for adding the WPS network.
+ doAnswer(new AnswerWithArguments() {
+ public boolean answer(Map<String, WifiConfiguration> configs,
+ SparseArray<Map<String, String>> networkExtras) throws Exception {
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = WPS_SUPPLICANT_NETWORK_ID;
+ config.SSID = DEFAULT_TEST_SSID;
+ configs.put("dummy", config);
+ return true;
+ }
+ }).when(mWifiNative).migrateNetworksFromSupplicant(any(Map.class), any(SparseArray.class));
+ when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+ .thenReturn(new NetworkUpdateResult(WPS_FRAMEWORK_NETWORK_ID));
+ when(mWifiConfigManager.enableNetwork(eq(WPS_FRAMEWORK_NETWORK_ID), anyBoolean(), anyInt()))
+ .thenReturn(true);
+ }
+
+ private void verifyMocksForWpsNetworkMigration() {
+ // Network Ids should be reset so that it is treated as addition.
+ ArgumentCaptor<WifiConfiguration> wifiConfigCaptor =
+ ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiConfigManager).addOrUpdateNetwork(wifiConfigCaptor.capture(), anyInt());
+ assertEquals(WifiConfiguration.INVALID_NETWORK_ID, wifiConfigCaptor.getValue().networkId);
+ assertEquals(DEFAULT_TEST_SSID, wifiConfigCaptor.getValue().SSID);
+ verify(mWifiConfigManager).enableNetwork(eq(WPS_FRAMEWORK_NETWORK_ID), anyBoolean(),
+ anyInt());
+ }
+
+ /**
+ * Verifies that WifiInfo is cleared upon exiting and entering WifiInfo, and that it is not
+ * updated by SUPPLICAN_STATE_CHANGE_EVENTs in ScanModeState.
+ * This protects WifiStateMachine from getting into a bad state where WifiInfo says wifi is
+ * already Connected or Connecting, (when it is in-fact Disconnected), so
+ * WifiConnectivityManager does not attempt any new Connections, freezing wifi.
+ */
+ @Test
+ public void testWifiInfoCleanedUpEnteringExitingConnectModeState() throws Exception {
+ InOrder inOrder = inOrder(mWifiConnectivityManager);
+ Log.i(TAG, mWsm.getCurrentState().getName());
+ String initialBSSID = "aa:bb:cc:dd:ee:ff";
+ WifiInfo wifiInfo = mWsm.getWifiInfo();
+ wifiInfo.setBSSID(initialBSSID);
+
+ // Set WSM to CONNECT_MODE and verify state, and wifi enabled in ConnectivityManager
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ startSupplicantAndDispatchMessages();
+ mWsm.setSupplicantRunning(true);
+ mLooper.dispatchAll();
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals(WifiManager.WIFI_STATE_ENABLED, mWsm.syncGetWifiState());
+ inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(true));
+ assertNull(wifiInfo.getBSSID());
+
+ // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is updated
+ mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+ new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
+ mLooper.dispatchAll();
+ assertEquals(sBSSID, wifiInfo.getBSSID());
+ assertEquals(SupplicantState.COMPLETED, wifiInfo.getSupplicantState());
+
+ // Set WSM to SCAN_ONLY_MODE, verify state and wifi disabled in ConnectivityManager, and
+ // WifiInfo is reset() and state set to DISCONNECTED
+ mWsm.setOperationalMode(WifiStateMachine.SCAN_ONLY_MODE);
+ mLooper.dispatchAll();
+ assertEquals(WifiStateMachine.SCAN_ONLY_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("ScanModeState", getCurrentState().getName());
+ assertEquals(WifiManager.WIFI_STATE_DISABLED, mWsm.syncGetWifiState());
+ inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(false));
+ assertNull(wifiInfo.getBSSID());
+ assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState());
+
+ // Send a SUPPLICANT_STATE_CHANGE_EVENT, verify WifiInfo is not updated
+ mWsm.sendMessage(WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0, 0,
+ new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED));
+ mLooper.dispatchAll();
+ assertNull(wifiInfo.getBSSID());
+ assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState());
+
+ // Set the bssid to something, so we can verify it is cleared (just in case)
+ wifiInfo.setBSSID(initialBSSID);
+
+ // Set WSM to CONNECT_MODE and verify state, and wifi enabled in ConnectivityManager,
+ // and WifiInfo has been reset
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ mLooper.dispatchAll();
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals(WifiManager.WIFI_STATE_ENABLED, mWsm.syncGetWifiState());
+ inOrder.verify(mWifiConnectivityManager).setWifiEnabled(eq(true));
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ assertEquals(SupplicantState.DISCONNECTED, wifiInfo.getSupplicantState());
+ assertNull(wifiInfo.getBSSID());
+ }
+
+ /**
+ * Adds the network without putting WifiStateMachine into ConnectMode.
+ */
+ @Test
+ public void addNetworkInInitialState() throws Exception {
+ // We should not be in initial state now.
+ assertTrue("InitialState".equals(getCurrentState().getName()));
+ addNetworkAndVerifySuccess(false);
+ verify(mWifiConnectivityManager, never()).setUserConnectChoice(eq(0));
+ }
+
+ /**
+ * Test START_WPS with a null wpsInfo object fails gracefully (No NPE)
+ */
+ @Test
+ public void testStartWps_nullWpsInfo() throws Exception {
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ mLooper.startAutoDispatch();
+ Message reply = mWsmAsyncChannel.sendMessageSynchronously(WifiManager.START_WPS, 0, 0,
+ null);
+ mLooper.stopAutoDispatch();
+ assertEquals(WifiManager.WPS_FAILED, reply.what);
+ }
+
+ /**
+ * Test that DISABLE_NETWORK returns failure to public API when WifiConfigManager returns
+ * failure.
+ */
+ @Test
+ public void testSyncDisableNetwork_failure() throws Exception {
+ loadComponentsInStaMode();
+ mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
+ assertEquals(WifiStateMachine.CONNECT_MODE, mWsm.getOperationalModeForTest());
+ assertEquals("DisconnectedState", getCurrentState().getName());
+ when(mWifiConfigManager.disableNetwork(anyInt(), anyInt())).thenReturn(false);
+
+ mLooper.startAutoDispatch();
+ boolean succeeded = mWsm.syncDisableNetwork(mWsmAsyncChannel, 0);
+ mLooper.stopAutoDispatch();
+ assertFalse(succeeded);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateTrackerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateTrackerTest.java
new file mode 100644
index 0000000..b6c8b8d
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateTrackerTest.java
@@ -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.
+ */
+
+package com.android.server.wifi;
+
+import com.android.internal.app.IBatteryStats;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+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 com.android.server.wifi.WifiStateTracker}.
+ */
+@SmallTest
+public class WifiStateTrackerTest {
+
+ private static final String TAG = "WifiStateTrackerTest";
+ @Mock IBatteryStats mBatteryStats;
+ private WifiStateTracker mWifiStateTracker;
+
+ /**
+ * Setup test.
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mWifiStateTracker = new WifiStateTracker(mBatteryStats);
+ }
+
+ /**
+ * Ensure BatteryStats's noteWifiState() is called when the method
+ * updateState() is invoked on WifiStateTracker for relevant states.
+ */
+ @Test
+ public void testBatteryStatsUpdated() throws Exception {
+ int[] relevantStates = new int[] { WifiStateTracker.SCAN_MODE,
+ WifiStateTracker.CONNECTED, WifiStateTracker.DISCONNECTED,
+ WifiStateTracker.SOFT_AP};
+ for (int i = 0; i < relevantStates.length; i++) {
+ mWifiStateTracker.updateState(relevantStates[i]);
+ }
+ verify(mBatteryStats, times(relevantStates.length)).noteWifiState(anyInt(), any());
+ }
+
+ /**
+ * Ensure BatteryStats's noteWifiState() is not called when the method
+ * updateState() is invoked on WifiStateTracker for irrelevant states.
+ */
+ @Test
+ public void testBatteryStatsNotUpdated() throws Exception {
+ int[] irrelevantStates = new int[] { WifiStateTracker.SCAN_MODE - 1,
+ WifiStateTracker.SOFT_AP + 1};
+ for (int i = 0; i < irrelevantStates.length; i++) {
+ mWifiStateTracker.updateState(irrelevantStates[i]);
+ }
+ verify(mBatteryStats, times(0)).noteWifiState(anyInt(), any());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
new file mode 100644
index 0000000..34ddf23
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -0,0 +1,1862 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.hardware.wifi.V1_0.IWifiApIface;
+import android.hardware.wifi.V1_0.IWifiChip;
+import android.hardware.wifi.V1_0.IWifiChipEventCallback;
+import android.hardware.wifi.V1_0.IWifiIface;
+import android.hardware.wifi.V1_0.IWifiRttController;
+import android.hardware.wifi.V1_0.IWifiRttControllerEventCallback;
+import android.hardware.wifi.V1_0.IWifiStaIface;
+import android.hardware.wifi.V1_0.IWifiStaIfaceEventCallback;
+import android.hardware.wifi.V1_0.RttCapabilities;
+import android.hardware.wifi.V1_0.RttConfig;
+import android.hardware.wifi.V1_0.StaApfPacketFilterCapabilities;
+import android.hardware.wifi.V1_0.StaBackgroundScanCapabilities;
+import android.hardware.wifi.V1_0.StaBackgroundScanParameters;
+import android.hardware.wifi.V1_0.StaLinkLayerIfacePacketStats;
+import android.hardware.wifi.V1_0.StaLinkLayerRadioStats;
+import android.hardware.wifi.V1_0.StaLinkLayerStats;
+import android.hardware.wifi.V1_0.StaScanData;
+import android.hardware.wifi.V1_0.StaScanDataFlagMask;
+import android.hardware.wifi.V1_0.StaScanResult;
+import android.hardware.wifi.V1_0.WifiDebugHostWakeReasonStats;
+import android.hardware.wifi.V1_0.WifiDebugPacketFateFrameType;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferFlags;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferStatus;
+import android.hardware.wifi.V1_0.WifiDebugRingBufferVerboseLevel;
+import android.hardware.wifi.V1_0.WifiDebugRxPacketFate;
+import android.hardware.wifi.V1_0.WifiDebugRxPacketFateReport;
+import android.hardware.wifi.V1_0.WifiDebugTxPacketFate;
+import android.hardware.wifi.V1_0.WifiDebugTxPacketFateReport;
+import android.hardware.wifi.V1_0.WifiInformationElement;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+import android.net.apf.ApfCapabilities;
+import android.net.wifi.RttManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiLinkLayerStats;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiScanner;
+import android.net.wifi.WifiSsid;
+import android.net.wifi.WifiWakeReasonAndCounts;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
+import android.util.Pair;
+
+import com.android.server.connectivity.KeepalivePacketData;
+import com.android.server.wifi.util.NativeUtil;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiVendorHal}.
+ */
+public class WifiVendorHalTest {
+
+ WifiVendorHal mWifiVendorHal;
+ private WifiStatus mWifiStatusSuccess;
+ private WifiStatus mWifiStatusFailure;
+ WifiLog mWifiLog;
+ @Mock
+ private HalDeviceManager mHalDeviceManager;
+ @Mock
+ private TestLooper mLooper;
+ @Mock
+ private WifiVendorHal.HalDeviceManagerStatusListener mHalDeviceManagerStatusCallbacks;
+ @Mock
+ private IWifiApIface mIWifiApIface;
+ @Mock
+ private IWifiChip mIWifiChip;
+ @Mock
+ private IWifiStaIface mIWifiStaIface;
+ @Mock
+ private IWifiRttController mIWifiRttController;
+ private IWifiStaIfaceEventCallback mIWifiStaIfaceEventCallback;
+ private IWifiChipEventCallback mIWifiChipEventCallback;
+ @Mock
+ private WifiNative.VendorHalDeathEventHandler mVendorHalDeathHandler;
+
+ /**
+ * Identity function to supply a type to its argument, which is a lambda
+ */
+ static Answer<WifiStatus> answerWifiStatus(Answer<WifiStatus> statusLambda) {
+ return (statusLambda);
+ }
+
+ /**
+ * Sets up for unit test
+ */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mWifiLog = new FakeWifiLog();
+ mLooper = new TestLooper();
+ mWifiStatusSuccess = new WifiStatus();
+ mWifiStatusSuccess.code = WifiStatusCode.SUCCESS;
+ mWifiStatusFailure = new WifiStatus();
+ mWifiStatusFailure.code = WifiStatusCode.ERROR_UNKNOWN;
+ mWifiStatusFailure.description = "I don't even know what a Mock Turtle is.";
+ when(mIWifiStaIface.enableLinkLayerStatsCollection(false)).thenReturn(mWifiStatusSuccess);
+
+ // Setup the HalDeviceManager mock's start/stop behaviour. This can be overridden in
+ // individual tests, if needed.
+ doAnswer(new AnswerWithArguments() {
+ public boolean answer() {
+ when(mHalDeviceManager.isReady()).thenReturn(true);
+ when(mHalDeviceManager.isStarted()).thenReturn(true);
+ mHalDeviceManagerStatusCallbacks.onStatusChanged();
+ return true;
+ }
+ }).when(mHalDeviceManager).start();
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer() {
+ when(mHalDeviceManager.isReady()).thenReturn(true);
+ when(mHalDeviceManager.isStarted()).thenReturn(false);
+ mHalDeviceManagerStatusCallbacks.onStatusChanged();
+ }
+ }).when(mHalDeviceManager).stop();
+ when(mHalDeviceManager.createStaIface(eq(null), eq(null)))
+ .thenReturn(mIWifiStaIface);
+ when(mHalDeviceManager.createApIface(eq(null), eq(null)))
+ .thenReturn(mIWifiApIface);
+ when(mHalDeviceManager.getChip(any(IWifiIface.class)))
+ .thenReturn(mIWifiChip);
+ when(mHalDeviceManager.createRttController(any(IWifiIface.class)))
+ .thenReturn(mIWifiRttController);
+ when(mIWifiChip.registerEventCallback(any(IWifiChipEventCallback.class)))
+ .thenReturn(mWifiStatusSuccess);
+ mIWifiStaIfaceEventCallback = null;
+ when(mIWifiStaIface.registerEventCallback(any(IWifiStaIfaceEventCallback.class)))
+ .thenAnswer(answerWifiStatus((invocation) -> {
+ Object[] args = invocation.getArguments();
+ mIWifiStaIfaceEventCallback = (IWifiStaIfaceEventCallback) args[0];
+ return (mWifiStatusSuccess);
+ }));
+ mIWifiChipEventCallback = null;
+ when(mIWifiChip.registerEventCallback(any(IWifiChipEventCallback.class)))
+ .thenAnswer(answerWifiStatus((invocation) -> {
+ Object[] args = invocation.getArguments();
+ mIWifiChipEventCallback = (IWifiChipEventCallback) args[0];
+ return (mWifiStatusSuccess);
+ }));
+
+ when(mIWifiRttController.registerEventCallback(any(IWifiRttControllerEventCallback.class)))
+ .thenReturn(mWifiStatusSuccess);
+
+ // Create the vendor HAL object under test.
+ mWifiVendorHal = new WifiVendorHal(mHalDeviceManager, mLooper.getLooper());
+
+ // Initialize the vendor HAL to capture the registered callback.
+ mWifiVendorHal.initialize(mVendorHalDeathHandler);
+ ArgumentCaptor<WifiVendorHal.HalDeviceManagerStatusListener> hdmCallbackCaptor =
+ ArgumentCaptor.forClass(WifiVendorHal.HalDeviceManagerStatusListener.class);
+ verify(mHalDeviceManager).registerStatusListener(hdmCallbackCaptor.capture(), any());
+ mHalDeviceManagerStatusCallbacks = hdmCallbackCaptor.getValue();
+
+ }
+
+ /**
+ * Tests the successful starting of HAL in STA mode using
+ * {@link WifiVendorHal#startVendorHal(boolean)}.
+ */
+ @Test
+ public void testStartHalSuccessInStaMode() throws Exception {
+ assertTrue(mWifiVendorHal.startVendorHal(true));
+ assertTrue(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager).getChip(eq(mIWifiStaIface));
+ verify(mHalDeviceManager).createRttController(eq(mIWifiStaIface));
+ verify(mHalDeviceManager).isReady();
+ verify(mHalDeviceManager).isStarted();
+ verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
+ verify(mIWifiChip).registerEventCallback(any(IWifiChipEventCallback.class));
+
+ verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+ }
+
+ /**
+ * Tests the successful starting of HAL in AP mode using
+ * {@link WifiVendorHal#startVendorHal(boolean)}.
+ */
+ @Test
+ public void testStartHalSuccessInApMode() throws Exception {
+ assertTrue(mWifiVendorHal.startVendorHal(false));
+ assertTrue(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+ verify(mHalDeviceManager).getChip(eq(mIWifiApIface));
+ verify(mHalDeviceManager).isReady();
+ verify(mHalDeviceManager).isStarted();
+
+ verify(mHalDeviceManager, never()).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager, never()).createRttController(any(IWifiIface.class));
+ }
+
+ /**
+ * Tests the failure to start HAL in STA mode using
+ * {@link WifiVendorHal#startVendorHal(boolean)}.
+ */
+ @Test
+ public void testStartHalFailureInStaMode() throws Exception {
+ // No callbacks are invoked in this case since the start itself failed. So, override
+ // default AnswerWithArguments that we setup.
+ doAnswer(new AnswerWithArguments() {
+ public boolean answer() throws Exception {
+ return false;
+ }
+ }).when(mHalDeviceManager).start();
+ assertFalse(mWifiVendorHal.startVendorHal(true));
+ assertFalse(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+
+ verify(mHalDeviceManager, never()).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+ verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
+ verify(mHalDeviceManager, never()).createRttController(any(IWifiIface.class));
+ verify(mIWifiStaIface, never())
+ .registerEventCallback(any(IWifiStaIfaceEventCallback.class));
+ }
+
+ /**
+ * Tests the failure to start HAL in STA mode using
+ * {@link WifiVendorHal#startVendorHal(boolean)}.
+ */
+ @Test
+ public void testStartHalFailureInIfaceCreationInStaMode() throws Exception {
+ when(mHalDeviceManager.createStaIface(eq(null), eq(null))).thenReturn(null);
+ assertFalse(mWifiVendorHal.startVendorHal(true));
+ assertFalse(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager).stop();
+
+ verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+ verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
+ verify(mHalDeviceManager, never()).createRttController(any(IWifiIface.class));
+ verify(mIWifiStaIface, never())
+ .registerEventCallback(any(IWifiStaIfaceEventCallback.class));
+ }
+
+ /**
+ * Tests the failure to start HAL in STA mode using
+ * {@link WifiVendorHal#startVendorHal(boolean)}.
+ */
+ @Test
+ public void testStartHalFailureInRttControllerCreationInStaMode() throws Exception {
+ when(mHalDeviceManager.createRttController(any(IWifiIface.class))).thenReturn(null);
+ assertFalse(mWifiVendorHal.startVendorHal(true));
+ assertFalse(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager).createRttController(eq(mIWifiStaIface));
+ verify(mHalDeviceManager).stop();
+ verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
+
+ verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+ verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
+ }
+
+ /**
+ * Tests the failure to start HAL in STA mode using
+ * {@link WifiVendorHal#startVendorHal(boolean)}.
+ */
+ @Test
+ public void testStartHalFailureInChipGetInStaMode() throws Exception {
+ when(mHalDeviceManager.getChip(any(IWifiIface.class))).thenReturn(null);
+ assertFalse(mWifiVendorHal.startVendorHal(true));
+ assertFalse(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager).createRttController(eq(mIWifiStaIface));
+ verify(mHalDeviceManager).getChip(any(IWifiIface.class));
+ verify(mHalDeviceManager).stop();
+ verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
+
+ verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+ }
+
+ /**
+ * Tests the failure to start HAL in STA mode using
+ * {@link WifiVendorHal#startVendorHal(boolean)}.
+ */
+ @Test
+ public void testStartHalFailureInStaIfaceCallbackRegistration() throws Exception {
+ when(mIWifiStaIface.registerEventCallback(any(IWifiStaIfaceEventCallback.class)))
+ .thenReturn(mWifiStatusFailure);
+ assertFalse(mWifiVendorHal.startVendorHal(true));
+ assertFalse(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager).stop();
+ verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
+
+ verify(mHalDeviceManager, never()).createRttController(eq(mIWifiStaIface));
+ verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
+ verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+ }
+
+ /**
+ * Tests the failure to start HAL in STA mode using
+ * {@link WifiVendorHal#startVendorHal(boolean)}.
+ */
+ @Test
+ public void testStartHalFailureInChipCallbackRegistration() throws Exception {
+ when(mIWifiChip.registerEventCallback(any(IWifiChipEventCallback.class)))
+ .thenReturn(mWifiStatusFailure);
+ assertFalse(mWifiVendorHal.startVendorHal(true));
+ assertFalse(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager).createRttController(eq(mIWifiStaIface));
+ verify(mHalDeviceManager).getChip(any(IWifiIface.class));
+ verify(mHalDeviceManager).stop();
+ verify(mIWifiStaIface).registerEventCallback(any(IWifiStaIfaceEventCallback.class));
+ verify(mIWifiChip).registerEventCallback(any(IWifiChipEventCallback.class));
+
+ verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+ }
+
+ /**
+ * Tests the failure to start HAL in STA mode using
+ * {@link WifiVendorHal#startVendorHal(boolean)}.
+ */
+ @Test
+ public void testStartHalFailureInApMode() throws Exception {
+ when(mHalDeviceManager.createApIface(eq(null), eq(null))).thenReturn(null);
+ assertFalse(mWifiVendorHal.startVendorHal(false));
+ assertFalse(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+ verify(mHalDeviceManager).stop();
+
+ verify(mHalDeviceManager, never()).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager, never()).getChip(any(IWifiIface.class));
+ verify(mHalDeviceManager, never()).createRttController(any(IWifiIface.class));
+ }
+
+ /**
+ * Tests the stopping of HAL in STA mode using
+ * {@link WifiVendorHal#stopVendorHal()}.
+ */
+ @Test
+ public void testStopHalInStaMode() {
+ assertTrue(mWifiVendorHal.startVendorHal(true));
+ assertTrue(mWifiVendorHal.isHalStarted());
+
+ mWifiVendorHal.stopVendorHal();
+ assertFalse(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).stop();
+ verify(mHalDeviceManager).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager).getChip(eq(mIWifiStaIface));
+ verify(mHalDeviceManager).createRttController(eq(mIWifiStaIface));
+ verify(mHalDeviceManager, times(2)).isReady();
+ verify(mHalDeviceManager, times(2)).isStarted();
+
+ verify(mHalDeviceManager, never()).createApIface(eq(null), eq(null));
+ }
+
+ /**
+ * Tests the stopping of HAL in AP mode using
+ * {@link WifiVendorHal#stopVendorHal()}.
+ */
+ @Test
+ public void testStopHalInApMode() {
+ assertTrue(mWifiVendorHal.startVendorHal(false));
+ assertTrue(mWifiVendorHal.isHalStarted());
+
+ mWifiVendorHal.stopVendorHal();
+ assertFalse(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ verify(mHalDeviceManager).stop();
+ verify(mHalDeviceManager).createApIface(eq(null), eq(null));
+ verify(mHalDeviceManager).getChip(eq(mIWifiApIface));
+ verify(mHalDeviceManager, times(2)).isReady();
+ verify(mHalDeviceManager, times(2)).isStarted();
+
+ verify(mHalDeviceManager, never()).createStaIface(eq(null), eq(null));
+ verify(mHalDeviceManager, never()).createRttController(any(IWifiIface.class));
+ }
+
+ /**
+ * Test that enter logs when verbose logging is enabled
+ */
+ @Test
+ public void testEnterLogging() {
+ mWifiVendorHal.mLog = spy(mWifiLog);
+ mWifiVendorHal.enableVerboseLogging(true);
+ mWifiVendorHal.installPacketFilter(new byte[0]);
+ verify(mWifiVendorHal.mLog).trace(eq("% filter length %"));
+ }
+
+ /**
+ * Test that enter does not log when verbose logging is not enabled
+ */
+ @Test
+ public void testEnterSilenceWhenNotEnabled() {
+ mWifiVendorHal.mLog = spy(mWifiLog);
+ mWifiVendorHal.installPacketFilter(new byte[0]);
+ mWifiVendorHal.enableVerboseLogging(true);
+ mWifiVendorHal.enableVerboseLogging(false);
+ mWifiVendorHal.installPacketFilter(new byte[0]);
+ verify(mWifiVendorHal.mLog, never()).trace(eq("% filter length %"));
+ }
+
+ /**
+ * Test that boolResult logs a false result
+ */
+ @Test
+ public void testBoolResultFalse() {
+ mWifiLog = spy(mWifiLog);
+ mWifiVendorHal.mLog = mWifiLog;
+ mWifiVendorHal.mVerboseLog = mWifiLog;
+ assertFalse(mWifiVendorHal.getBgScanCapabilities(new WifiNative.ScanCapabilities()));
+ verify(mWifiLog).err("% returns %");
+ }
+
+ /**
+ * Test that getBgScanCapabilities is hooked up to the HAL correctly
+ *
+ * A call before the vendor HAL is started should return a non-null result with version 0
+ *
+ * A call after the HAL is started should return the mocked values.
+ */
+ @Test
+ public void testGetBgScanCapabilities() throws Exception {
+ StaBackgroundScanCapabilities capabilities = new StaBackgroundScanCapabilities();
+ capabilities.maxCacheSize = 12;
+ capabilities.maxBuckets = 34;
+ capabilities.maxApCachePerScan = 56;
+ capabilities.maxReportingThreshold = 78;
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiStaIface.getBackgroundScanCapabilitiesCallback cb)
+ throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, capabilities);
+ }
+ }).when(mIWifiStaIface).getBackgroundScanCapabilities(any(
+ IWifiStaIface.getBackgroundScanCapabilitiesCallback.class));
+
+ WifiNative.ScanCapabilities result = new WifiNative.ScanCapabilities();
+
+ assertFalse(mWifiVendorHal.getBgScanCapabilities(result)); // should fail - not started
+ assertTrue(mWifiVendorHal.startVendorHalSta()); // Start the vendor hal
+ assertTrue(mWifiVendorHal.getBgScanCapabilities(result)); // should succeed
+
+ assertEquals(12, result.max_scan_cache_size);
+ assertEquals(34, result.max_scan_buckets);
+ assertEquals(56, result.max_ap_cache_per_scan);
+ assertEquals(78, result.max_scan_reporting_threshold);
+ }
+
+ private void setupValidFrequenciesForBand(ArrayList<Integer> frequencies) throws Exception {
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(int band, IWifiStaIface.getValidFrequenciesForBandCallback cb)
+ throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, frequencies);
+ }
+ }).when(mIWifiStaIface).getValidFrequenciesForBand(anyInt(), any(
+ IWifiStaIface.getValidFrequenciesForBandCallback.class));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(int band, IWifiApIface.getValidFrequenciesForBandCallback cb)
+ throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, frequencies);
+ }
+ }).when(mIWifiApIface).getValidFrequenciesForBand(anyInt(), any(
+ IWifiApIface.getValidFrequenciesForBandCallback.class));
+
+ }
+
+ private int[] intArrayFromArrayList(ArrayList<Integer> in) {
+ int[] ans = new int[in.size()];
+ int i = 0;
+ for (Integer e : in) ans[i++] = e;
+ return ans;
+ }
+
+ /**
+ * Test that isGetChannelsForBandSupported works in STA mode
+ */
+ @Test
+ public void testGetChannelsForBandSupportedSta() throws Exception {
+ ArrayList<Integer> freq = new ArrayList<>();
+ freq.add(2405);
+
+ setupValidFrequenciesForBand(freq);
+
+ assertFalse(mWifiVendorHal.isGetChannelsForBandSupported());
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertTrue(mWifiVendorHal.isGetChannelsForBandSupported());
+ }
+
+ /**
+ * Test that isGetChannelsForBandSupported works in AP mode
+ */
+ @Test
+ public void testGetChannelsForBandSupportedAp() throws Exception {
+ ArrayList<Integer> freq = new ArrayList<>();
+ freq.add(2405);
+
+ setupValidFrequenciesForBand(freq);
+
+ assertFalse(mWifiVendorHal.isGetChannelsForBandSupported());
+
+ assertTrue(mWifiVendorHal.startVendorHalAp());
+
+ assertTrue(mWifiVendorHal.isGetChannelsForBandSupported());
+ }
+
+ /**
+ * Test translation to WifiManager.WIFI_FEATURE_*
+ *
+ * Just do a spot-check with a few feature bits here; since the code is table-
+ * driven we don't have to work hard to exercise all of it.
+ */
+ @Test
+ public void testFeatureMaskTranslation() {
+ int caps = (
+ IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN
+ | IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS
+ );
+ int expected = (
+ WifiManager.WIFI_FEATURE_SCANNER
+ | WifiManager.WIFI_FEATURE_LINK_LAYER_STATS);
+ assertEquals(expected, mWifiVendorHal.wifiFeatureMaskFromStaCapabilities(caps));
+ }
+
+ /**
+ * Test enablement of link layer stats after startup
+ *
+ * Request link layer stats before HAL start
+ * - should not make it to the HAL layer
+ * Start the HAL in STA mode
+ * Request link layer stats twice more
+ * - enable request should make it to the HAL layer
+ * - HAL layer should have been called to make the requests (i.e., two calls total)
+ */
+ @Test
+ public void testLinkLayerStatsEnableAfterStartup() throws Exception {
+ doNothing().when(mIWifiStaIface).getLinkLayerStats(any());
+
+ assertNull(mWifiVendorHal.getWifiLinkLayerStats());
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertTrue(mWifiVendorHal.isHalStarted());
+
+ verify(mHalDeviceManager).start();
+ mWifiVendorHal.getWifiLinkLayerStats();
+ mWifiVendorHal.getWifiLinkLayerStats();
+ verify(mIWifiStaIface).enableLinkLayerStatsCollection(false); // mLinkLayerStatsDebug
+ verify(mIWifiStaIface, times(2)).getLinkLayerStats(any());
+ }
+
+ /**
+ * Test that link layer stats are not enabled and harmless in AP mode
+ *
+ * Start the HAL in AP mode
+ * - stats should not be enabled
+ * Request link layer stats
+ * - HAL layer should have been called to make the request
+ */
+ @Test
+ public void testLinkLayerStatsNotEnabledAndHarmlessInApMode() throws Exception {
+ doNothing().when(mIWifiStaIface).getLinkLayerStats(any());
+
+ assertTrue(mWifiVendorHal.startVendorHalAp());
+ assertTrue(mWifiVendorHal.isHalStarted());
+ assertNull(mWifiVendorHal.getWifiLinkLayerStats());
+
+ verify(mHalDeviceManager).start();
+
+ verify(mIWifiStaIface, never()).enableLinkLayerStatsCollection(false);
+ verify(mIWifiStaIface, never()).getLinkLayerStats(any());
+ }
+
+ /**
+ * Test that the link layer stats fields are populated correctly.
+ *
+ * This is done by filling with random values and then using toString on the
+ * original and converted values, comparing just the numerics in the result.
+ * This makes the assumption that the fields are in the same order in both string
+ * representations, which is not quite true. So apply some fixups before the final
+ * comparison.
+ */
+ @Test
+ public void testLinkLayerStatsAssignment() throws Exception {
+ Random r = new Random(1775968256);
+ StaLinkLayerStats stats = new StaLinkLayerStats();
+ randomizePacketStats(r, stats.iface.wmeBePktStats);
+ randomizePacketStats(r, stats.iface.wmeBkPktStats);
+ randomizePacketStats(r, stats.iface.wmeViPktStats);
+ randomizePacketStats(r, stats.iface.wmeVoPktStats);
+ randomizeRadioStats(r, stats.radios);
+
+ stats.timeStampInMs = 42; // currently dropped in conversion
+
+ String expected = numbersOnly(stats.toString());
+
+ WifiLinkLayerStats converted = WifiVendorHal.frameworkFromHalLinkLayerStats(stats);
+
+ String actual = numbersOnly(converted.toString());
+
+ // Do the required fixups to the both expected and actual
+ expected = rmValue(expected, stats.radios.get(0).rxTimeInMs);
+ expected = rmValue(expected, stats.radios.get(0).onTimeInMsForScan);
+
+ actual = rmValue(actual, stats.radios.get(0).rxTimeInMs);
+ actual = rmValue(actual, stats.radios.get(0).onTimeInMsForScan);
+ actual = actual + "42 ";
+
+ // The remaining fields should agree
+ assertEquals(expected, actual);
+ }
+
+ /** Just the digits with delimiting spaces, please */
+ private static String numbersOnly(String s) {
+ return s.replaceAll("[^0-9]+", " ");
+ }
+
+ /** Remove the given value from the space-delimited string, or die trying. */
+ private static String rmValue(String s, long value) throws Exception {
+ String ans = s.replaceAll(" " + value + " ", " ");
+ assertNotEquals(s, ans);
+ return ans;
+ }
+
+ /**
+ * Populate packet stats with non-negative random values
+ */
+ private static void randomizePacketStats(Random r, StaLinkLayerIfacePacketStats pstats) {
+ pstats.rxMpdu = r.nextLong() & 0xFFFFFFFFFFL; // more than 32 bits
+ pstats.txMpdu = r.nextLong() & 0xFFFFFFFFFFL;
+ pstats.lostMpdu = r.nextLong() & 0xFFFFFFFFFFL;
+ pstats.retries = r.nextLong() & 0xFFFFFFFFFFL;
+ }
+
+ /**
+ * Populate radio stats with non-negative random values
+ */
+ private static void randomizeRadioStats(Random r, ArrayList<StaLinkLayerRadioStats> rstats) {
+ StaLinkLayerRadioStats rstat = new StaLinkLayerRadioStats();
+ rstat.onTimeInMs = r.nextInt() & 0xFFFFFF;
+ rstat.txTimeInMs = r.nextInt() & 0xFFFFFF;
+ for (int i = 0; i < 4; i++) {
+ Integer v = r.nextInt() & 0xFFFFFF;
+ rstat.txTimeInMsPerLevel.add(v);
+ }
+ rstat.rxTimeInMs = r.nextInt() & 0xFFFFFF;
+ rstat.onTimeInMsForScan = r.nextInt() & 0xFFFFFF;
+ rstats.add(rstat);
+ }
+
+ /**
+ * Test that getFirmwareVersion() and getDriverVersion() work
+ *
+ * Calls before the STA is started are expected to return null.
+ */
+ @Test
+ public void testVersionGetters() throws Exception {
+ String firmwareVersion = "fuzzy";
+ String driverVersion = "dizzy";
+ IWifiChip.ChipDebugInfo chipDebugInfo = new IWifiChip.ChipDebugInfo();
+ chipDebugInfo.firmwareDescription = firmwareVersion;
+ chipDebugInfo.driverDescription = driverVersion;
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiChip.requestChipDebugInfoCallback cb) throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, chipDebugInfo);
+ }
+ }).when(mIWifiChip).requestChipDebugInfo(any(IWifiChip.requestChipDebugInfoCallback.class));
+
+ assertNull(mWifiVendorHal.getFirmwareVersion());
+ assertNull(mWifiVendorHal.getDriverVersion());
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertEquals(firmwareVersion, mWifiVendorHal.getFirmwareVersion());
+ assertEquals(driverVersion, mWifiVendorHal.getDriverVersion());
+ }
+
+ /**
+ * For checkRoundTripIntTranslation lambdas
+ */
+ interface IntForInt {
+ int translate(int value);
+ }
+
+ /**
+ * Checks that translation from x to y and back again is the identity function
+ *
+ * @param xFromY reverse translator
+ * @param yFromX forward translator
+ * @param xLimit non-inclusive upper bound on x (lower bound is zero)
+ */
+ private void checkRoundTripIntTranslation(
+ IntForInt xFromY, IntForInt yFromX, int xFirst, int xLimit) throws Exception {
+ int ex = 0;
+ for (int i = xFirst; i < xLimit; i++) {
+ assertEquals(i, xFromY.translate(yFromX.translate(i)));
+ }
+ try {
+ yFromX.translate(xLimit);
+ assertTrue("expected an exception here", false);
+ } catch (IllegalArgumentException e) {
+ ex++;
+ }
+ try {
+ xFromY.translate(yFromX.translate(xLimit - 1) + 1);
+ assertTrue("expected an exception here", false);
+ } catch (IllegalArgumentException e) {
+ ex++;
+ }
+ assertEquals(2, ex);
+ }
+
+
+ /**
+ * Test translations of RTT type
+ */
+ @Test
+ public void testRttTypeTranslation() throws Exception {
+ checkRoundTripIntTranslation(
+ (y) -> WifiVendorHal.halRttTypeFromFrameworkRttType(y),
+ (x) -> WifiVendorHal.frameworkRttTypeFromHalRttType(x),
+ 1, 3);
+ }
+
+ /**
+ * Test translations of peer type
+ */
+ @Test
+ public void testPeerTranslation() throws Exception {
+ checkRoundTripIntTranslation(
+ (y) -> WifiVendorHal.halPeerFromFrameworkPeer(y),
+ (x) -> WifiVendorHal.frameworkPeerFromHalPeer(x),
+ 1, 6);
+ }
+
+ /**
+ * Test translations of channel width
+ */
+ @Test
+ public void testChannelWidth() throws Exception {
+ checkRoundTripIntTranslation(
+ (y) -> WifiVendorHal.halChannelWidthFromFrameworkChannelWidth(y),
+ (x) -> WifiVendorHal.frameworkChannelWidthFromHalChannelWidth(x),
+ 0, 5);
+ }
+
+ /**
+ * Test translations of preamble type mask
+ */
+ @Test
+ public void testPreambleTranslation() throws Exception {
+ checkRoundTripIntTranslation(
+ (y) -> WifiVendorHal.halPreambleFromFrameworkPreamble(y),
+ (x) -> WifiVendorHal.frameworkPreambleFromHalPreamble(x),
+ 0, 8);
+ }
+
+ /**
+ * Test translations of bandwidth mask
+ */
+ @Test
+ public void testBandwidthTranslations() throws Exception {
+ checkRoundTripIntTranslation(
+ (y) -> WifiVendorHal.halBwFromFrameworkBw(y),
+ (x) -> WifiVendorHal.frameworkBwFromHalBw(x),
+ 0, 64);
+ }
+
+ @Test
+ public void testGetRttStuff() throws Exception {
+ RttManager.RttParams params = new RttManager.RttParams();
+ //TODO(b/34901744) populate
+ RttConfig config = WifiVendorHal.halRttConfigFromFrameworkRttParams(params);
+ //TODO(b/34901744) check
+ }
+
+ @Test
+ public void testGetRttCapabilities() throws Exception {
+ RttCapabilities capabilities = new RttCapabilities();
+ //TODO(b/34901744) populate
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiRttController.getCapabilitiesCallback cb)
+ throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, capabilities);
+ }
+ }).when(mIWifiRttController).getCapabilities(any(
+ IWifiRttController.getCapabilitiesCallback.class));
+
+ assertNull(mWifiVendorHal.getRttCapabilities());
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ RttManager.RttCapabilities actual = mWifiVendorHal.getRttCapabilities();
+ //TODO(b/34901744) check
+
+ }
+
+ //TODO(b/34901744) negative RTT test cases as well.
+ // e.g. invoke RTT without putting the HAL in the correct mode.
+
+ /**
+ * Test that setScanningMacOui is hooked up to the HAL correctly
+ */
+ @Test
+ public void testSetScanningMacOui() throws Exception {
+ byte[] oui = NativeUtil.macAddressOuiToByteArray("DA:A1:19");
+ byte[] zzz = NativeUtil.macAddressOuiToByteArray("00:00:00");
+
+ when(mIWifiStaIface.setScanningMacOui(any())).thenReturn(mWifiStatusSuccess);
+
+ assertFalse(mWifiVendorHal.setScanningMacOui(oui)); // expect fail - STA not started
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertFalse(mWifiVendorHal.setScanningMacOui(null)); // expect fail - null
+ assertFalse(mWifiVendorHal.setScanningMacOui(new byte[]{(byte) 1})); // expect fail - len
+ assertTrue(mWifiVendorHal.setScanningMacOui(oui));
+ assertTrue(mWifiVendorHal.setScanningMacOui(zzz));
+
+ verify(mIWifiStaIface).setScanningMacOui(eq(oui));
+ verify(mIWifiStaIface).setScanningMacOui(eq(zzz));
+ }
+
+ @Test
+ public void testStartSendingOffloadedPacket() throws Exception {
+ byte[] srcMac = NativeUtil.macAddressToByteArray("4007b2088c81");
+ InetAddress src = InetAddress.parseNumericAddress("192.168.13.13");
+ InetAddress dst = InetAddress.parseNumericAddress("93.184.216.34");
+ int slot = 13;
+ int millis = 16000;
+
+ KeepalivePacketData kap = KeepalivePacketData.nattKeepalivePacket(src, 63000, dst, 4500);
+
+ when(mIWifiStaIface.startSendingKeepAlivePackets(
+ anyInt(), any(), anyShort(), any(), any(), anyInt()
+ )).thenReturn(mWifiStatusSuccess);
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertTrue(0 == mWifiVendorHal.startSendingOffloadedPacket(slot, srcMac, kap, millis));
+
+ verify(mIWifiStaIface).startSendingKeepAlivePackets(
+ eq(slot), any(), anyShort(), any(), any(), eq(millis));
+ }
+
+ @Test
+ public void testStopSendingOffloadedPacket() throws Exception {
+ int slot = 13;
+
+ when(mIWifiStaIface.stopSendingKeepAlivePackets(anyInt())).thenReturn(mWifiStatusSuccess);
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertTrue(0 == mWifiVendorHal.stopSendingOffloadedPacket(slot));
+
+ verify(mIWifiStaIface).stopSendingKeepAlivePackets(eq(slot));
+ }
+
+ /**
+ * Test the setup, invocation, and removal of a RSSI event handler
+ *
+ */
+ @Test
+ public void testRssiMonitoring() throws Exception {
+ when(mIWifiStaIface.startRssiMonitoring(anyInt(), anyInt(), anyInt()))
+ .thenReturn(mWifiStatusSuccess);
+ when(mIWifiStaIface.stopRssiMonitoring(anyInt()))
+ .thenReturn(mWifiStatusSuccess);
+
+ ArrayList<Byte> breach = new ArrayList<>(10);
+ byte hi = -21;
+ byte med = -42;
+ byte lo = -84;
+ Byte lower = -88;
+ WifiNative.WifiRssiEventHandler handler;
+ handler = ((cur) -> {
+ breach.add(cur);
+ });
+ assertEquals(-1, mWifiVendorHal.startRssiMonitoring(hi, lo, handler)); // not started
+ assertEquals(-1, mWifiVendorHal.stopRssiMonitoring()); // not started
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertEquals(0, mWifiVendorHal.startRssiMonitoring(hi, lo, handler));
+ int theCmdId = mWifiVendorHal.sRssiMonCmdId;
+ breach.clear();
+ mIWifiStaIfaceEventCallback.onRssiThresholdBreached(theCmdId, new byte[6], lower);
+ assertEquals(breach.get(0), lower);
+ assertEquals(0, mWifiVendorHal.stopRssiMonitoring());
+ assertEquals(0, mWifiVendorHal.startRssiMonitoring(hi, lo, handler));
+ assertEquals(0, mWifiVendorHal.startRssiMonitoring(med, lo, handler)); // replacing works
+ assertEquals(-1, mWifiVendorHal.startRssiMonitoring(hi, lo, null)); // null handler fails
+ assertEquals(0, mWifiVendorHal.startRssiMonitoring(hi, lo, handler));
+ assertEquals(-1, mWifiVendorHal.startRssiMonitoring(lo, hi, handler)); // empty range
+ }
+
+ /**
+ * Test that getApfCapabilities is hooked up to the HAL correctly
+ *
+ * A call before the vendor HAL is started should return a non-null result with version 0
+ *
+ * A call after the HAL is started should return the mocked values.
+ */
+ @Test
+ public void testApfCapabilities() throws Exception {
+ int myVersion = 33;
+ int myMaxSize = 1234;
+
+ StaApfPacketFilterCapabilities capabilities = new StaApfPacketFilterCapabilities();
+ capabilities.version = myVersion;
+ capabilities.maxLength = myMaxSize;
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiStaIface.getApfPacketFilterCapabilitiesCallback cb)
+ throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, capabilities);
+ }
+ }).when(mIWifiStaIface).getApfPacketFilterCapabilities(any(
+ IWifiStaIface.getApfPacketFilterCapabilitiesCallback.class));
+
+
+ assertEquals(0, mWifiVendorHal.getApfCapabilities().apfVersionSupported);
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ ApfCapabilities actual = mWifiVendorHal.getApfCapabilities();
+
+ assertEquals(myVersion, actual.apfVersionSupported);
+ assertEquals(myMaxSize, actual.maximumApfProgramSize);
+ assertEquals(android.system.OsConstants.ARPHRD_ETHER, actual.apfPacketFormat);
+ assertNotEquals(0, actual.apfPacketFormat);
+ }
+
+ /**
+ * Test that an APF program can be installed.
+ */
+ @Test
+ public void testInstallApf() throws Exception {
+ byte[] filter = new byte[] {19, 53, 10};
+
+ ArrayList<Byte> expected = new ArrayList<>(3);
+ for (byte b : filter) expected.add(b);
+
+ when(mIWifiStaIface.installApfPacketFilter(anyInt(), any(ArrayList.class)))
+ .thenReturn(mWifiStatusSuccess);
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertTrue(mWifiVendorHal.installPacketFilter(filter));
+
+ verify(mIWifiStaIface).installApfPacketFilter(eq(0), eq(expected));
+ }
+
+ /**
+ * Test that the country code is set in AP mode (when it should be).
+ */
+ @Test
+ public void testSetCountryCodeHal() throws Exception {
+ byte[] expected = new byte[]{(byte) 'C', (byte) 'A'};
+
+ when(mIWifiApIface.setCountryCode(any()))
+ .thenReturn(mWifiStatusSuccess);
+
+ assertTrue(mWifiVendorHal.startVendorHalAp());
+
+ assertFalse(mWifiVendorHal.setCountryCodeHal(null));
+ assertFalse(mWifiVendorHal.setCountryCodeHal(""));
+ assertFalse(mWifiVendorHal.setCountryCodeHal("A"));
+ assertTrue(mWifiVendorHal.setCountryCodeHal("CA")); // Only one expected to succeed
+ assertFalse(mWifiVendorHal.setCountryCodeHal("ZZZ"));
+
+ verify(mIWifiApIface).setCountryCode(eq(expected));
+ }
+
+ /**
+ * Test that RemoteException is caught and logged.
+ */
+ @Test
+ public void testRemoteExceptionIsHandled() throws Exception {
+ mWifiLog = spy(mWifiLog);
+ mWifiVendorHal.mVerboseLog = mWifiLog;
+ when(mIWifiApIface.setCountryCode(any()))
+ .thenThrow(new RemoteException("oops"));
+ assertTrue(mWifiVendorHal.startVendorHalAp());
+ assertFalse(mWifiVendorHal.setCountryCodeHal("CA"));
+ assertFalse(mWifiVendorHal.isHalStarted());
+ verify(mWifiLog).err(any());
+ }
+
+ /**
+ * Test that startLoggingToDebugRingBuffer is plumbed to chip
+ *
+ * A call before the vendor hal is started should just return false.
+ * After starting in STA mode, the call should succeed, and pass ther right things down.
+ */
+ @Test
+ public void testStartLoggingRingBuffer() throws Exception {
+ when(mIWifiChip.startLoggingToDebugRingBuffer(
+ any(String.class), anyInt(), anyInt(), anyInt()
+ )).thenReturn(mWifiStatusSuccess);
+
+ assertFalse(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 0, 0, "One"));
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertTrue(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 11, 3000, "One"));
+
+ verify(mIWifiChip).startLoggingToDebugRingBuffer("One", 1, 11, 3000);
+ }
+
+ /**
+ * Same test as testStartLoggingRingBuffer, but in AP mode rather than STA.
+ */
+ @Test
+ public void testStartLoggingRingBufferOnAp() throws Exception {
+ when(mIWifiChip.startLoggingToDebugRingBuffer(
+ any(String.class), anyInt(), anyInt(), anyInt()
+ )).thenReturn(mWifiStatusSuccess);
+
+ assertFalse(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 0, 0, "One"));
+ assertTrue(mWifiVendorHal.startVendorHalAp());
+ assertTrue(mWifiVendorHal.startLoggingRingBuffer(1, 0x42, 11, 3000, "One"));
+
+ verify(mIWifiChip).startLoggingToDebugRingBuffer("One", 1, 11, 3000);
+ }
+
+ /**
+ * Test that getRingBufferStatus gets and translates its stuff correctly
+ */
+ @Test
+ public void testRingBufferStatus() throws Exception {
+ WifiDebugRingBufferStatus one = new WifiDebugRingBufferStatus();
+ one.ringName = "One";
+ one.flags = WifiDebugRingBufferFlags.HAS_BINARY_ENTRIES;
+ one.ringId = 5607371;
+ one.sizeInBytes = 54321;
+ one.freeSizeInBytes = 42;
+ one.verboseLevel = WifiDebugRingBufferVerboseLevel.VERBOSE;
+ String oneExpect = "name: One flag: 1 ringBufferId: 5607371 ringBufferByteSize: 54321"
+ + " verboseLevel: 2 writtenBytes: 0 readBytes: 0 writtenRecords: 0";
+
+ WifiDebugRingBufferStatus two = new WifiDebugRingBufferStatus();
+ two.ringName = "Two";
+ two.flags = WifiDebugRingBufferFlags.HAS_ASCII_ENTRIES
+ | WifiDebugRingBufferFlags.HAS_PER_PACKET_ENTRIES;
+ two.ringId = 4512470;
+ two.sizeInBytes = 300;
+ two.freeSizeInBytes = 42;
+ two.verboseLevel = WifiDebugRingBufferVerboseLevel.DEFAULT;
+
+ ArrayList<WifiDebugRingBufferStatus> halBufferStatus = new ArrayList<>(2);
+ halBufferStatus.add(one);
+ halBufferStatus.add(two);
+
+ WifiNative.RingBufferStatus[] actual;
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiChip.getDebugRingBuffersStatusCallback cb)
+ throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, halBufferStatus);
+ }
+ }).when(mIWifiChip).getDebugRingBuffersStatus(any(
+ IWifiChip.getDebugRingBuffersStatusCallback.class));
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ actual = mWifiVendorHal.getRingBufferStatus();
+
+ assertEquals(halBufferStatus.size(), actual.length);
+ assertEquals(oneExpect, actual[0].toString());
+ assertEquals(two.ringId, actual[1].ringBufferId);
+
+ }
+
+ /**
+ * Test that getRingBufferData calls forceDumpToDebugRingBuffer
+ *
+ * Try once before hal start, and twice after (one success, one failure).
+ */
+ @Test
+ public void testForceRingBufferDump() throws Exception {
+ when(mIWifiChip.forceDumpToDebugRingBuffer(eq("Gunk"))).thenReturn(mWifiStatusSuccess);
+ when(mIWifiChip.forceDumpToDebugRingBuffer(eq("Glop"))).thenReturn(mWifiStatusFailure);
+
+ assertFalse(mWifiVendorHal.getRingBufferData("Gunk")); // hal not started
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertTrue(mWifiVendorHal.getRingBufferData("Gunk")); // mocked call succeeds
+ assertFalse(mWifiVendorHal.getRingBufferData("Glop")); // mocked call fails
+
+ verify(mIWifiChip).forceDumpToDebugRingBuffer("Gunk");
+ verify(mIWifiChip).forceDumpToDebugRingBuffer("Glop");
+ }
+
+ /**
+ * Tests the start of packet fate monitoring.
+ *
+ * Try once before hal start, and once after (one success, one failure).
+ */
+ @Test
+ public void testStartPktFateMonitoring() throws Exception {
+ when(mIWifiStaIface.startDebugPacketFateMonitoring()).thenReturn(mWifiStatusSuccess);
+
+ assertFalse(mWifiVendorHal.startPktFateMonitoring());
+ verify(mIWifiStaIface, never()).startDebugPacketFateMonitoring();
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertTrue(mWifiVendorHal.startPktFateMonitoring());
+ verify(mIWifiStaIface).startDebugPacketFateMonitoring();
+ }
+
+ /**
+ * Tests the retrieval of tx packet fates.
+ *
+ * Try once before hal start, and once after.
+ */
+ @Test
+ public void testGetTxPktFates() throws Exception {
+ byte[] frameContentBytes = new byte[30];
+ new Random().nextBytes(frameContentBytes);
+ WifiDebugTxPacketFateReport fateReport = new WifiDebugTxPacketFateReport();
+ fateReport.fate = WifiDebugTxPacketFate.DRV_QUEUED;
+ fateReport.frameInfo.driverTimestampUsec = new Random().nextLong();
+ fateReport.frameInfo.frameType = WifiDebugPacketFateFrameType.ETHERNET_II;
+ fateReport.frameInfo.frameContent.addAll(
+ NativeUtil.byteArrayToArrayList(frameContentBytes));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiStaIface.getDebugTxPacketFatesCallback cb) {
+ cb.onValues(mWifiStatusSuccess,
+ new ArrayList<WifiDebugTxPacketFateReport>(Arrays.asList(fateReport)));
+ }
+ }).when(mIWifiStaIface)
+ .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
+
+ WifiNative.TxFateReport[] retrievedFates = new WifiNative.TxFateReport[1];
+ assertFalse(mWifiVendorHal.getTxPktFates(retrievedFates));
+ verify(mIWifiStaIface, never())
+ .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertTrue(mWifiVendorHal.getTxPktFates(retrievedFates));
+ verify(mIWifiStaIface)
+ .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
+ assertEquals(WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED, retrievedFates[0].mFate);
+ assertEquals(fateReport.frameInfo.driverTimestampUsec,
+ retrievedFates[0].mDriverTimestampUSec);
+ assertEquals(WifiLoggerHal.FRAME_TYPE_ETHERNET_II, retrievedFates[0].mFrameType);
+ assertArrayEquals(frameContentBytes, retrievedFates[0].mFrameBytes);
+ }
+
+ /**
+ * Tests the retrieval of tx packet fates when the number of fates retrieved exceeds the
+ * input array.
+ *
+ * Try once before hal start, and once after.
+ */
+ @Test
+ public void testGetTxPktFatesExceedsInputArrayLength() throws Exception {
+ byte[] frameContentBytes = new byte[30];
+ new Random().nextBytes(frameContentBytes);
+ WifiDebugTxPacketFateReport fateReport = new WifiDebugTxPacketFateReport();
+ fateReport.fate = WifiDebugTxPacketFate.FW_DROP_OTHER;
+ fateReport.frameInfo.driverTimestampUsec = new Random().nextLong();
+ fateReport.frameInfo.frameType = WifiDebugPacketFateFrameType.MGMT_80211;
+ fateReport.frameInfo.frameContent.addAll(
+ NativeUtil.byteArrayToArrayList(frameContentBytes));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiStaIface.getDebugTxPacketFatesCallback cb) {
+ cb.onValues(mWifiStatusSuccess,
+ new ArrayList<WifiDebugTxPacketFateReport>(Arrays.asList(
+ fateReport, fateReport)));
+ }
+ }).when(mIWifiStaIface)
+ .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
+
+ WifiNative.TxFateReport[] retrievedFates = new WifiNative.TxFateReport[1];
+ assertFalse(mWifiVendorHal.getTxPktFates(retrievedFates));
+ verify(mIWifiStaIface, never())
+ .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertTrue(mWifiVendorHal.getTxPktFates(retrievedFates));
+ verify(mIWifiStaIface)
+ .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
+ assertEquals(WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER, retrievedFates[0].mFate);
+ assertEquals(fateReport.frameInfo.driverTimestampUsec,
+ retrievedFates[0].mDriverTimestampUSec);
+ assertEquals(WifiLoggerHal.FRAME_TYPE_80211_MGMT, retrievedFates[0].mFrameType);
+ assertArrayEquals(frameContentBytes, retrievedFates[0].mFrameBytes);
+ }
+
+ /**
+ * Tests the retrieval of rx packet fates.
+ *
+ * Try once before hal start, and once after.
+ */
+ @Test
+ public void testGetRxPktFates() throws Exception {
+ byte[] frameContentBytes = new byte[30];
+ new Random().nextBytes(frameContentBytes);
+ WifiDebugRxPacketFateReport fateReport = new WifiDebugRxPacketFateReport();
+ fateReport.fate = WifiDebugRxPacketFate.SUCCESS;
+ fateReport.frameInfo.driverTimestampUsec = new Random().nextLong();
+ fateReport.frameInfo.frameType = WifiDebugPacketFateFrameType.ETHERNET_II;
+ fateReport.frameInfo.frameContent.addAll(
+ NativeUtil.byteArrayToArrayList(frameContentBytes));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiStaIface.getDebugRxPacketFatesCallback cb) {
+ cb.onValues(mWifiStatusSuccess,
+ new ArrayList<WifiDebugRxPacketFateReport>(Arrays.asList(fateReport)));
+ }
+ }).when(mIWifiStaIface)
+ .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
+
+ WifiNative.RxFateReport[] retrievedFates = new WifiNative.RxFateReport[1];
+ assertFalse(mWifiVendorHal.getRxPktFates(retrievedFates));
+ verify(mIWifiStaIface, never())
+ .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertTrue(mWifiVendorHal.getRxPktFates(retrievedFates));
+ verify(mIWifiStaIface)
+ .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
+ assertEquals(WifiLoggerHal.RX_PKT_FATE_SUCCESS, retrievedFates[0].mFate);
+ assertEquals(fateReport.frameInfo.driverTimestampUsec,
+ retrievedFates[0].mDriverTimestampUSec);
+ assertEquals(WifiLoggerHal.FRAME_TYPE_ETHERNET_II, retrievedFates[0].mFrameType);
+ assertArrayEquals(frameContentBytes, retrievedFates[0].mFrameBytes);
+ }
+
+ /**
+ * Tests the retrieval of rx packet fates when the number of fates retrieved exceeds the
+ * input array.
+ *
+ * Try once before hal start, and once after.
+ */
+ @Test
+ public void testGetRxPktFatesExceedsInputArrayLength() throws Exception {
+ byte[] frameContentBytes = new byte[30];
+ new Random().nextBytes(frameContentBytes);
+ WifiDebugRxPacketFateReport fateReport = new WifiDebugRxPacketFateReport();
+ fateReport.fate = WifiDebugRxPacketFate.FW_DROP_FILTER;
+ fateReport.frameInfo.driverTimestampUsec = new Random().nextLong();
+ fateReport.frameInfo.frameType = WifiDebugPacketFateFrameType.MGMT_80211;
+ fateReport.frameInfo.frameContent.addAll(
+ NativeUtil.byteArrayToArrayList(frameContentBytes));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiStaIface.getDebugRxPacketFatesCallback cb) {
+ cb.onValues(mWifiStatusSuccess,
+ new ArrayList<WifiDebugRxPacketFateReport>(Arrays.asList(
+ fateReport, fateReport)));
+ }
+ }).when(mIWifiStaIface)
+ .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
+
+ WifiNative.RxFateReport[] retrievedFates = new WifiNative.RxFateReport[1];
+ assertFalse(mWifiVendorHal.getRxPktFates(retrievedFates));
+ verify(mIWifiStaIface, never())
+ .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertTrue(mWifiVendorHal.getRxPktFates(retrievedFates));
+ verify(mIWifiStaIface)
+ .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
+ assertEquals(WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER, retrievedFates[0].mFate);
+ assertEquals(fateReport.frameInfo.driverTimestampUsec,
+ retrievedFates[0].mDriverTimestampUSec);
+ assertEquals(WifiLoggerHal.FRAME_TYPE_80211_MGMT, retrievedFates[0].mFrameType);
+ assertArrayEquals(frameContentBytes, retrievedFates[0].mFrameBytes);
+ }
+
+ /**
+ * Tests the failure to retrieve tx packet fates when the input array is empty.
+ */
+ @Test
+ public void testGetTxPktFatesEmptyInputArray() throws Exception {
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertFalse(mWifiVendorHal.getTxPktFates(new WifiNative.TxFateReport[0]));
+ verify(mIWifiStaIface, never())
+ .getDebugTxPacketFates(any(IWifiStaIface.getDebugTxPacketFatesCallback.class));
+ }
+
+ /**
+ * Tests the failure to retrieve rx packet fates when the input array is empty.
+ */
+ @Test
+ public void testGetRxPktFatesEmptyInputArray() throws Exception {
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertFalse(mWifiVendorHal.getRxPktFates(new WifiNative.RxFateReport[0]));
+ verify(mIWifiStaIface, never())
+ .getDebugRxPacketFates(any(IWifiStaIface.getDebugRxPacketFatesCallback.class));
+ }
+
+ /**
+ * Tests the nd offload enable/disable.
+ */
+ @Test
+ public void testEnableDisableNdOffload() throws Exception {
+ when(mIWifiStaIface.enableNdOffload(anyBoolean())).thenReturn(mWifiStatusSuccess);
+
+ assertFalse(mWifiVendorHal.configureNeighborDiscoveryOffload(true));
+ verify(mIWifiStaIface, never()).enableNdOffload(anyBoolean());
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertTrue(mWifiVendorHal.configureNeighborDiscoveryOffload(true));
+ verify(mIWifiStaIface).enableNdOffload(eq(true));
+ assertTrue(mWifiVendorHal.configureNeighborDiscoveryOffload(false));
+ verify(mIWifiStaIface).enableNdOffload(eq(false));
+ }
+
+ /**
+ * Tests the nd offload enable failure.
+ */
+ @Test
+ public void testEnableNdOffloadFailure() throws Exception {
+ when(mIWifiStaIface.enableNdOffload(eq(true))).thenReturn(mWifiStatusFailure);
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertFalse(mWifiVendorHal.configureNeighborDiscoveryOffload(true));
+ verify(mIWifiStaIface).enableNdOffload(eq(true));
+ }
+
+ /**
+ * Tests the retrieval of wlan wake reason stats.
+ */
+ @Test
+ public void testGetWlanWakeReasonCount() throws Exception {
+ WifiDebugHostWakeReasonStats stats = new WifiDebugHostWakeReasonStats();
+ Random rand = new Random();
+ stats.totalCmdEventWakeCnt = rand.nextInt();
+ stats.totalDriverFwLocalWakeCnt = rand.nextInt();
+ stats.totalRxPacketWakeCnt = rand.nextInt();
+ stats.rxPktWakeDetails.rxUnicastCnt = rand.nextInt();
+ stats.rxPktWakeDetails.rxMulticastCnt = rand.nextInt();
+ stats.rxIcmpPkWakeDetails.icmpPkt = rand.nextInt();
+ stats.rxIcmpPkWakeDetails.icmp6Pkt = rand.nextInt();
+ stats.rxMulticastPkWakeDetails.ipv4RxMulticastAddrCnt = rand.nextInt();
+ stats.rxMulticastPkWakeDetails.ipv6RxMulticastAddrCnt = rand.nextInt();
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiChip.getDebugHostWakeReasonStatsCallback cb) {
+ cb.onValues(mWifiStatusSuccess, stats);
+ }
+ }).when(mIWifiChip).getDebugHostWakeReasonStats(
+ any(IWifiChip.getDebugHostWakeReasonStatsCallback.class));
+
+ assertNull(mWifiVendorHal.getWlanWakeReasonCount());
+ verify(mIWifiChip, never())
+ .getDebugHostWakeReasonStats(
+ any(IWifiChip.getDebugHostWakeReasonStatsCallback.class));
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ WifiWakeReasonAndCounts retrievedStats = mWifiVendorHal.getWlanWakeReasonCount();
+ verify(mIWifiChip).getDebugHostWakeReasonStats(
+ any(IWifiChip.getDebugHostWakeReasonStatsCallback.class));
+ assertNotNull(retrievedStats);
+ assertEquals(stats.totalCmdEventWakeCnt, retrievedStats.totalCmdEventWake);
+ assertEquals(stats.totalDriverFwLocalWakeCnt, retrievedStats.totalDriverFwLocalWake);
+ assertEquals(stats.totalRxPacketWakeCnt, retrievedStats.totalRxDataWake);
+ assertEquals(stats.rxPktWakeDetails.rxUnicastCnt, retrievedStats.rxUnicast);
+ assertEquals(stats.rxPktWakeDetails.rxMulticastCnt, retrievedStats.rxMulticast);
+ assertEquals(stats.rxIcmpPkWakeDetails.icmpPkt, retrievedStats.icmp);
+ assertEquals(stats.rxIcmpPkWakeDetails.icmp6Pkt, retrievedStats.icmp6);
+ assertEquals(stats.rxMulticastPkWakeDetails.ipv4RxMulticastAddrCnt,
+ retrievedStats.ipv4RxMulticast);
+ assertEquals(stats.rxMulticastPkWakeDetails.ipv6RxMulticastAddrCnt,
+ retrievedStats.ipv6Multicast);
+ }
+
+ /**
+ * Tests the failure in retrieval of wlan wake reason stats.
+ */
+ @Test
+ public void testGetWlanWakeReasonCountFailure() throws Exception {
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiChip.getDebugHostWakeReasonStatsCallback cb) {
+ cb.onValues(mWifiStatusFailure, new WifiDebugHostWakeReasonStats());
+ }
+ }).when(mIWifiChip).getDebugHostWakeReasonStats(
+ any(IWifiChip.getDebugHostWakeReasonStatsCallback.class));
+
+ // This should work in both AP & STA mode.
+ assertTrue(mWifiVendorHal.startVendorHalAp());
+
+ assertNull(mWifiVendorHal.getWlanWakeReasonCount());
+ verify(mIWifiChip).getDebugHostWakeReasonStats(
+ any(IWifiChip.getDebugHostWakeReasonStatsCallback.class));
+ }
+
+ /**
+ * Test that getFwMemoryDump is properly plumbed
+ */
+ @Test
+ public void testGetFwMemoryDump() throws Exception {
+ byte [] sample = NativeUtil.hexStringToByteArray("268c7a3fbfa4661c0bdd6a36");
+ ArrayList<Byte> halBlob = NativeUtil.byteArrayToArrayList(sample);
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiChip.requestFirmwareDebugDumpCallback cb)
+ throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, halBlob);
+ }
+ }).when(mIWifiChip).requestFirmwareDebugDump(any(
+ IWifiChip.requestFirmwareDebugDumpCallback.class));
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertArrayEquals(sample, mWifiVendorHal.getFwMemoryDump());
+ }
+
+ /**
+ * Test that getDriverStateDump is properly plumbed
+ *
+ * Just for variety, use AP mode here.
+ */
+ @Test
+ public void testGetDriverStateDump() throws Exception {
+ byte [] sample = NativeUtil.hexStringToByteArray("e83ff543cf80083e6459d20f");
+ ArrayList<Byte> halBlob = NativeUtil.byteArrayToArrayList(sample);
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(IWifiChip.requestDriverDebugDumpCallback cb)
+ throws RemoteException {
+ cb.onValues(mWifiStatusSuccess, halBlob);
+ }
+ }).when(mIWifiChip).requestDriverDebugDump(any(
+ IWifiChip.requestDriverDebugDumpCallback.class));
+
+ assertTrue(mWifiVendorHal.startVendorHalAp());
+ assertArrayEquals(sample, mWifiVendorHal.getDriverStateDump());
+ }
+
+ /**
+ * Test that background scan failure is handled correctly.
+ */
+ @Test
+ public void testBgScanFailureCallback() throws Exception {
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertNotNull(mIWifiStaIfaceEventCallback);
+
+ WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
+ startBgScan(eventHandler);
+
+ mIWifiStaIfaceEventCallback.onBackgroundScanFailure(mWifiVendorHal.mScan.cmdId);
+ verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_FAILED);
+ }
+
+ /**
+ * Test that background scan failure with wrong id is not reported.
+ */
+ @Test
+ public void testBgScanFailureCallbackWithInvalidCmdId() throws Exception {
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertNotNull(mIWifiStaIfaceEventCallback);
+
+ WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
+ startBgScan(eventHandler);
+
+ mIWifiStaIfaceEventCallback.onBackgroundScanFailure(mWifiVendorHal.mScan.cmdId + 1);
+ verify(eventHandler, never()).onScanStatus(WifiNative.WIFI_SCAN_FAILED);
+ }
+
+ /**
+ * Test that background scan full results are handled correctly.
+ */
+ @Test
+ public void testBgScanFullScanResults() throws Exception {
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertNotNull(mIWifiStaIfaceEventCallback);
+
+ WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
+ startBgScan(eventHandler);
+
+ Pair<StaScanResult, ScanResult> result = createHidlAndFrameworkBgScanResult();
+ mIWifiStaIfaceEventCallback.onBackgroundFullScanResult(
+ mWifiVendorHal.mScan.cmdId, 5, result.first);
+
+ ArgumentCaptor<ScanResult> scanResultCaptor = ArgumentCaptor.forClass(ScanResult.class);
+ verify(eventHandler).onFullScanResult(scanResultCaptor.capture(), eq(5));
+
+ assertScanResultEqual(result.second, scanResultCaptor.getValue());
+ }
+
+ /**
+ * Test that background scan results are handled correctly.
+ */
+ @Test
+ public void testBgScanScanResults() throws Exception {
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertNotNull(mIWifiStaIfaceEventCallback);
+
+ WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
+ startBgScan(eventHandler);
+
+ Pair<ArrayList<StaScanData>, ArrayList<WifiScanner.ScanData>> data =
+ createHidlAndFrameworkBgScanDatas();
+ mIWifiStaIfaceEventCallback.onBackgroundScanResults(
+ mWifiVendorHal.mScan.cmdId, data.first);
+
+ verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
+ assertScanDatasEqual(
+ data.second, Arrays.asList(mWifiVendorHal.mScan.latestScanResults));
+ }
+
+ /**
+ * Test that starting a new background scan when one is active will stop the previous one.
+ */
+ @Test
+ public void testBgScanReplacement() throws Exception {
+ when(mIWifiStaIface.stopBackgroundScan(anyInt())).thenReturn(mWifiStatusSuccess);
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertNotNull(mIWifiStaIfaceEventCallback);
+ WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
+ startBgScan(eventHandler);
+ int cmdId1 = mWifiVendorHal.mScan.cmdId;
+ startBgScan(eventHandler);
+ assertNotEquals(mWifiVendorHal.mScan.cmdId, cmdId1);
+ verify(mIWifiStaIface, times(2)).startBackgroundScan(anyInt(), any());
+ verify(mIWifiStaIface).stopBackgroundScan(cmdId1);
+ }
+
+ /**
+ * Test stopping a background scan.
+ */
+ @Test
+ public void testBgScanStop() throws Exception {
+ when(mIWifiStaIface.stopBackgroundScan(anyInt())).thenReturn(mWifiStatusSuccess);
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertNotNull(mIWifiStaIfaceEventCallback);
+ WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
+ startBgScan(eventHandler);
+
+ int cmdId = mWifiVendorHal.mScan.cmdId;
+
+ mWifiVendorHal.stopBgScan();
+ mWifiVendorHal.stopBgScan(); // second call should not do anything
+ verify(mIWifiStaIface).stopBackgroundScan(cmdId); // Should be called just once
+ }
+
+ /**
+ * Test pausing and restarting a background scan.
+ */
+ @Test
+ public void testBgScanPauseAndRestart() throws Exception {
+ when(mIWifiStaIface.stopBackgroundScan(anyInt())).thenReturn(mWifiStatusSuccess);
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertNotNull(mIWifiStaIfaceEventCallback);
+ WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
+ startBgScan(eventHandler);
+
+ int cmdId = mWifiVendorHal.mScan.cmdId;
+
+ mWifiVendorHal.pauseBgScan();
+ mWifiVendorHal.restartBgScan();
+ verify(mIWifiStaIface).stopBackgroundScan(cmdId); // Should be called just once
+ verify(mIWifiStaIface, times(2)).startBackgroundScan(eq(cmdId), any());
+ }
+
+ /**
+ * Test the handling of log handler set.
+ */
+ @Test
+ public void testSetLogHandler() throws Exception {
+ when(mIWifiChip.enableDebugErrorAlerts(anyBoolean())).thenReturn(mWifiStatusSuccess);
+
+ WifiNative.WifiLoggerEventHandler eventHandler =
+ mock(WifiNative.WifiLoggerEventHandler.class);
+
+ assertFalse(mWifiVendorHal.setLoggingEventHandler(eventHandler));
+ verify(mIWifiChip, never()).enableDebugErrorAlerts(anyBoolean());
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ assertTrue(mWifiVendorHal.setLoggingEventHandler(eventHandler));
+ verify(mIWifiChip).enableDebugErrorAlerts(eq(true));
+ reset(mIWifiChip);
+
+ // Second call should fail.
+ assertFalse(mWifiVendorHal.setLoggingEventHandler(eventHandler));
+ verify(mIWifiChip, never()).enableDebugErrorAlerts(anyBoolean());
+ }
+
+ /**
+ * Test the handling of log handler reset.
+ */
+ @Test
+ public void testResetLogHandler() throws Exception {
+ when(mIWifiChip.enableDebugErrorAlerts(anyBoolean())).thenReturn(mWifiStatusSuccess);
+ when(mIWifiChip.stopLoggingToDebugRingBuffer()).thenReturn(mWifiStatusSuccess);
+
+ assertFalse(mWifiVendorHal.resetLogHandler());
+ verify(mIWifiChip, never()).enableDebugErrorAlerts(anyBoolean());
+ verify(mIWifiChip, never()).stopLoggingToDebugRingBuffer();
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+
+ // Not set, so this should fail.
+ assertFalse(mWifiVendorHal.resetLogHandler());
+ verify(mIWifiChip, never()).enableDebugErrorAlerts(anyBoolean());
+ verify(mIWifiChip, never()).stopLoggingToDebugRingBuffer();
+
+ // Now set and then reset.
+ assertTrue(mWifiVendorHal.setLoggingEventHandler(
+ mock(WifiNative.WifiLoggerEventHandler.class)));
+ assertTrue(mWifiVendorHal.resetLogHandler());
+ verify(mIWifiChip).enableDebugErrorAlerts(eq(false));
+ verify(mIWifiChip).stopLoggingToDebugRingBuffer();
+ reset(mIWifiChip);
+
+ // Second reset should fail.
+ assertFalse(mWifiVendorHal.resetLogHandler());
+ verify(mIWifiChip, never()).enableDebugErrorAlerts(anyBoolean());
+ verify(mIWifiChip, never()).stopLoggingToDebugRingBuffer();
+ }
+
+ /**
+ * Test the handling of alert callback.
+ */
+ @Test
+ public void testAlertCallback() throws Exception {
+ when(mIWifiChip.enableDebugErrorAlerts(anyBoolean())).thenReturn(mWifiStatusSuccess);
+ when(mIWifiChip.stopLoggingToDebugRingBuffer()).thenReturn(mWifiStatusSuccess);
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertNotNull(mIWifiChipEventCallback);
+
+ int errorCode = 5;
+ byte[] errorData = new byte[45];
+ new Random().nextBytes(errorData);
+
+ // Randomly raise the HIDL callback before we register for the log callback.
+ // This should be safely ignored. (Not trigger NPE.)
+ mIWifiChipEventCallback.onDebugErrorAlert(
+ errorCode, NativeUtil.byteArrayToArrayList(errorData));
+ mLooper.dispatchAll();
+
+ WifiNative.WifiLoggerEventHandler eventHandler =
+ mock(WifiNative.WifiLoggerEventHandler.class);
+ assertTrue(mWifiVendorHal.setLoggingEventHandler(eventHandler));
+ verify(mIWifiChip).enableDebugErrorAlerts(eq(true));
+
+ // Now raise the HIDL callback, this should be properly handled.
+ mIWifiChipEventCallback.onDebugErrorAlert(
+ errorCode, NativeUtil.byteArrayToArrayList(errorData));
+ mLooper.dispatchAll();
+ verify(eventHandler).onWifiAlert(eq(errorCode), eq(errorData));
+
+ // Now stop the logging and invoke the callback. This should be ignored.
+ reset(eventHandler);
+ assertTrue(mWifiVendorHal.resetLogHandler());
+ mIWifiChipEventCallback.onDebugErrorAlert(
+ errorCode, NativeUtil.byteArrayToArrayList(errorData));
+ mLooper.dispatchAll();
+ verify(eventHandler, never()).onWifiAlert(anyInt(), anyObject());
+ }
+
+ /**
+ * Test the handling of ring buffer callback.
+ */
+ @Test
+ public void testRingBufferDataCallback() throws Exception {
+ when(mIWifiChip.enableDebugErrorAlerts(anyBoolean())).thenReturn(mWifiStatusSuccess);
+ when(mIWifiChip.stopLoggingToDebugRingBuffer()).thenReturn(mWifiStatusSuccess);
+
+ assertTrue(mWifiVendorHal.startVendorHalSta());
+ assertNotNull(mIWifiChipEventCallback);
+
+ byte[] errorData = new byte[45];
+ new Random().nextBytes(errorData);
+
+ // Randomly raise the HIDL callback before we register for the log callback.
+ // This should be safely ignored. (Not trigger NPE.)
+ mIWifiChipEventCallback.onDebugRingBufferDataAvailable(
+ new WifiDebugRingBufferStatus(), NativeUtil.byteArrayToArrayList(errorData));
+ mLooper.dispatchAll();
+
+ WifiNative.WifiLoggerEventHandler eventHandler =
+ mock(WifiNative.WifiLoggerEventHandler.class);
+ assertTrue(mWifiVendorHal.setLoggingEventHandler(eventHandler));
+ verify(mIWifiChip).enableDebugErrorAlerts(eq(true));
+
+ // Now raise the HIDL callback, this should be properly handled.
+ mIWifiChipEventCallback.onDebugRingBufferDataAvailable(
+ new WifiDebugRingBufferStatus(), NativeUtil.byteArrayToArrayList(errorData));
+ mLooper.dispatchAll();
+ verify(eventHandler).onRingBufferData(
+ any(WifiNative.RingBufferStatus.class), eq(errorData));
+
+ // Now stop the logging and invoke the callback. This should be ignored.
+ reset(eventHandler);
+ assertTrue(mWifiVendorHal.resetLogHandler());
+ mIWifiChipEventCallback.onDebugRingBufferDataAvailable(
+ new WifiDebugRingBufferStatus(), NativeUtil.byteArrayToArrayList(errorData));
+ mLooper.dispatchAll();
+ verify(eventHandler, never()).onRingBufferData(anyObject(), anyObject());
+ }
+
+ /**
+ * Test the handling of Vendor HAL death.
+ */
+ @Test
+ public void testVendorHalDeath() {
+ // Invoke the HAL device manager status callback with ready set to false to indicate the
+ // death of the HAL.
+ when(mHalDeviceManager.isReady()).thenReturn(false);
+ mHalDeviceManagerStatusCallbacks.onStatusChanged();
+
+ verify(mVendorHalDeathHandler).onDeath();
+ }
+
+ private void startBgScan(WifiNative.ScanEventHandler eventHandler) throws Exception {
+ when(mIWifiStaIface.startBackgroundScan(
+ anyInt(), any(StaBackgroundScanParameters.class))).thenReturn(mWifiStatusSuccess);
+ WifiNative.ScanSettings settings = new WifiNative.ScanSettings();
+ settings.num_buckets = 1;
+ WifiNative.BucketSettings bucketSettings = new WifiNative.BucketSettings();
+ bucketSettings.bucket = 0;
+ bucketSettings.period_ms = 16000;
+ bucketSettings.report_events = WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN;
+ settings.buckets = new WifiNative.BucketSettings[] {bucketSettings};
+ assertTrue(mWifiVendorHal.startBgScan(settings, eventHandler));
+ }
+
+ // Create a pair of HIDL scan result and its corresponding framework scan result for
+ // comparison.
+ private Pair<StaScanResult, ScanResult> createHidlAndFrameworkBgScanResult() {
+ StaScanResult staScanResult = new StaScanResult();
+ Random random = new Random();
+ byte[] ssid = new byte[8];
+ random.nextBytes(ssid);
+ staScanResult.ssid.addAll(NativeUtil.byteArrayToArrayList(ssid));
+ random.nextBytes(staScanResult.bssid);
+ staScanResult.frequency = 2432;
+ staScanResult.rssi = -45;
+ staScanResult.timeStampInUs = 5;
+ WifiInformationElement ie1 = new WifiInformationElement();
+ byte[] ie1_data = new byte[56];
+ random.nextBytes(ie1_data);
+ ie1.id = 1;
+ ie1.data.addAll(NativeUtil.byteArrayToArrayList(ie1_data));
+ staScanResult.informationElements.add(ie1);
+
+ // Now create the corresponding Scan result structure.
+ ScanResult scanResult = new ScanResult();
+ scanResult.SSID = NativeUtil.encodeSsid(staScanResult.ssid);
+ scanResult.BSSID = NativeUtil.macAddressFromByteArray(staScanResult.bssid);
+ scanResult.wifiSsid = WifiSsid.createFromByteArray(ssid);
+ scanResult.frequency = staScanResult.frequency;
+ scanResult.level = staScanResult.rssi;
+ scanResult.timestamp = staScanResult.timeStampInUs;
+ scanResult.bytes = new byte[57];
+ scanResult.bytes[0] = ie1.id;
+ System.arraycopy(ie1_data, 0, scanResult.bytes, 1, ie1_data.length);
+
+ return Pair.create(staScanResult, scanResult);
+ }
+
+ // Create a pair of HIDL scan datas and its corresponding framework scan datas for
+ // comparison.
+ private Pair<ArrayList<StaScanData>, ArrayList<WifiScanner.ScanData>>
+ createHidlAndFrameworkBgScanDatas() {
+ ArrayList<StaScanData> staScanDatas = new ArrayList<>();
+ StaScanData staScanData = new StaScanData();
+
+ Pair<StaScanResult, ScanResult> result = createHidlAndFrameworkBgScanResult();
+ staScanData.results.add(result.first);
+ staScanData.bucketsScanned = 5;
+ staScanData.flags = StaScanDataFlagMask.INTERRUPTED;
+ staScanDatas.add(staScanData);
+
+ ArrayList<WifiScanner.ScanData> scanDatas = new ArrayList<>();
+ ScanResult[] scanResults = new ScanResult[1];
+ scanResults[0] = result.second;
+ WifiScanner.ScanData scanData =
+ new WifiScanner.ScanData(mWifiVendorHal.mScan.cmdId, 1,
+ staScanData.bucketsScanned, false, scanResults);
+ scanDatas.add(scanData);
+ return Pair.create(staScanDatas, scanDatas);
+ }
+
+ private void assertScanResultEqual(ScanResult expected, ScanResult actual) {
+ assertEquals(expected.SSID, actual.SSID);
+ assertEquals(expected.wifiSsid.getHexString(), actual.wifiSsid.getHexString());
+ assertEquals(expected.BSSID, actual.BSSID);
+ assertEquals(expected.frequency, actual.frequency);
+ assertEquals(expected.level, actual.level);
+ assertEquals(expected.timestamp, actual.timestamp);
+ assertArrayEquals(expected.bytes, actual.bytes);
+ }
+
+ private void assertScanResultsEqual(ScanResult[] expected, ScanResult[] actual) {
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; i++) {
+ assertScanResultEqual(expected[i], actual[i]);
+ }
+ }
+
+ private void assertScanDataEqual(WifiScanner.ScanData expected, WifiScanner.ScanData actual) {
+ assertEquals(expected.getId(), actual.getId());
+ assertEquals(expected.getFlags(), actual.getFlags());
+ assertEquals(expected.getBucketsScanned(), actual.getBucketsScanned());
+ assertScanResultsEqual(expected.getResults(), actual.getResults());
+ }
+
+ private void assertScanDatasEqual(
+ List<WifiScanner.ScanData> expected, List<WifiScanner.ScanData> actual) {
+ assertEquals(expected.size(), actual.size());
+ for (int i = 0; i < expected.size(); i++) {
+ assertScanDataEqual(expected.get(i), actual.get(i));
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java b/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java
new file mode 100644
index 0000000..2617331
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/WificondControlTest.java
@@ -0,0 +1,739 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.wifi.IApInterface;
+import android.net.wifi.IClientInterface;
+import android.net.wifi.IPnoScanEvent;
+import android.net.wifi.IScanEvent;
+import android.net.wifi.IWifiScannerImpl;
+import android.net.wifi.IWificond;
+import android.net.wifi.WifiScanner;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.util.NativeUtil;
+import com.android.server.wifi.wificond.ChannelSettings;
+import com.android.server.wifi.wificond.HiddenNetwork;
+import com.android.server.wifi.wificond.NativeScanResult;
+import com.android.server.wifi.wificond.PnoSettings;
+import com.android.server.wifi.wificond.SingleScanSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WificondControl}.
+ */
+@SmallTest
+public class WificondControlTest {
+ private WifiInjector mWifiInjector;
+ private WifiMonitor mWifiMonitor;
+ private WificondControl mWificondControl;
+ private static final String TEST_INTERFACE_NAME = "test_wlan_if";
+ private static final byte[] TEST_SSID =
+ new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'};
+ private static final byte[] TEST_BSSID =
+ new byte[] {(byte) 0x12, (byte) 0xef, (byte) 0xa1,
+ (byte) 0x2c, (byte) 0x97, (byte) 0x8b};
+ // This the IE buffer which is consistent with TEST_SSID.
+ private static final byte[] TEST_INFO_ELEMENT =
+ new byte[] {
+ // Element ID for SSID.
+ (byte) 0x00,
+ // Length of the SSID: 0x0b or 11.
+ (byte) 0x0b,
+ // This is string "GoogleGuest"
+ 'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'};
+
+ private static final int TEST_FREQUENCY = 2456;
+ private static final int TEST_SIGNAL_MBM = -4500;
+ private static final long TEST_TSF = 34455441;
+ private static final BitSet TEST_CAPABILITY = new BitSet(16) {{ set(2); set(5); }};
+ private static final boolean TEST_ASSOCIATED = true;
+ private static final NativeScanResult MOCK_NATIVE_SCAN_RESULT =
+ new NativeScanResult() {{
+ ssid = TEST_SSID;
+ bssid = TEST_BSSID;
+ infoElement = TEST_INFO_ELEMENT;
+ frequency = TEST_FREQUENCY;
+ signalMbm = TEST_SIGNAL_MBM;
+ capability = TEST_CAPABILITY;
+ associated = TEST_ASSOCIATED;
+ }};
+
+ private static final Set<Integer> SCAN_FREQ_SET =
+ new HashSet<Integer>() {{
+ add(2410);
+ add(2450);
+ add(5050);
+ add(5200);
+ }};
+ private static final String TEST_QUOTED_SSID_1 = "\"testSsid1\"";
+ private static final String TEST_QUOTED_SSID_2 = "\"testSsid2\"";
+
+ private static final Set<String> SCAN_HIDDEN_NETWORK_SSID_SET =
+ new HashSet<String>() {{
+ add(TEST_QUOTED_SSID_1);
+ add(TEST_QUOTED_SSID_2);
+ }};
+
+
+ private static final WifiNative.PnoSettings TEST_PNO_SETTINGS =
+ new WifiNative.PnoSettings() {{
+ isConnected = false;
+ periodInMs = 6000;
+ networkList = new WifiNative.PnoNetwork[2];
+ networkList[0] = new WifiNative.PnoNetwork();
+ networkList[1] = new WifiNative.PnoNetwork();
+ networkList[0].ssid = TEST_QUOTED_SSID_1;
+ networkList[0].flags = WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN;
+ networkList[1].ssid = TEST_QUOTED_SSID_2;
+ networkList[1].flags = 0;
+ }};
+
+ @Before
+ public void setUp() throws Exception {
+ mWifiInjector = mock(WifiInjector.class);
+ mWifiMonitor = mock(WifiMonitor.class);
+ mWificondControl = new WificondControl(mWifiInjector, mWifiMonitor);
+ }
+
+ /**
+ * Verifies that setupDriverForClientMode() calls Wificond.
+ */
+ @Test
+ public void testSetupDriverForClientMode() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+
+ IClientInterface returnedClientInterface = mWificondControl.setupDriverForClientMode();
+ assertEquals(clientInterface, returnedClientInterface);
+ verify(wificond).createClientInterface();
+ }
+
+ /**
+ * Verifies that setupDriverForClientMode() calls subscribeScanEvents().
+ */
+ @Test
+ public void testSetupDriverForClientModeCallsScanEventSubscripiton() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ verify(scanner).subscribeScanEvents(any(IScanEvent.class));
+ }
+
+ /**
+ * Verifies that setupDriverForClientMode() returns null when wificond is not started.
+ */
+ @Test
+ public void testSetupDriverForClientModeErrorWhenWificondIsNotStarted() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(null);
+
+ IClientInterface returnedClientInterface = mWificondControl.setupDriverForClientMode();
+ assertEquals(null, returnedClientInterface);
+ }
+
+ /**
+ * Verifies that setupDriverForClientMode() returns null when wificond failed to setup client
+ * interface.
+ */
+ @Test
+ public void testSetupDriverForClientModeErrorWhenWificondFailedToSetupInterface()
+ throws Exception {
+ IWificond wificond = mock(IWificond.class);
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(null);
+
+ IClientInterface returnedClientInterface = mWificondControl.setupDriverForClientMode();
+ assertEquals(null, returnedClientInterface);
+ }
+
+ /**
+ * Verifies that setupDriverForSoftApMode() calls wificond.
+ */
+ @Test
+ public void testSetupDriverForSoftApMode() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IApInterface apInterface = mock(IApInterface.class);
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createApInterface()).thenReturn(apInterface);
+
+ IApInterface returnedApInterface = mWificondControl.setupDriverForSoftApMode();
+ assertEquals(apInterface, returnedApInterface);
+ verify(wificond).createApInterface();
+ }
+
+ /**
+ * Verifies that setupDriverForSoftAp() returns null when wificond is not started.
+ */
+ @Test
+ public void testSetupDriverForSoftApModeErrorWhenWificondIsNotStarted() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(null);
+
+ IApInterface returnedApInterface = mWificondControl.setupDriverForSoftApMode();
+
+ assertEquals(null, returnedApInterface);
+ }
+
+ /**
+ * Verifies that setupDriverForSoftApMode() returns null when wificond failed to setup
+ * AP interface.
+ */
+ @Test
+ public void testSetupDriverForSoftApModeErrorWhenWificondFailedToSetupInterface()
+ throws Exception {
+ IWificond wificond = mock(IWificond.class);
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createApInterface()).thenReturn(null);
+
+ IApInterface returnedApInterface = mWificondControl.setupDriverForSoftApMode();
+ assertEquals(null, returnedApInterface);
+ }
+
+ /**
+ * Verifies that enableSupplicant() calls wificond.
+ */
+ @Test
+ public void testEnableSupplicant() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+ when(clientInterface.enableSupplicant()).thenReturn(true);
+
+ mWificondControl.setupDriverForClientMode();
+ assertTrue(mWificondControl.enableSupplicant());
+ verify(clientInterface).enableSupplicant();
+ }
+
+ /**
+ * Verifies that enableSupplicant() returns false when there is no configured
+ * client interface.
+ */
+ @Test
+ public void testEnableSupplicantErrorWhenNoClientInterfaceConfigured() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+
+ // Configure client interface.
+ IClientInterface returnedClientInterface = mWificondControl.setupDriverForClientMode();
+ assertEquals(clientInterface, returnedClientInterface);
+
+ // Tear down interfaces.
+ assertTrue(mWificondControl.tearDownInterfaces());
+
+ // Enabling supplicant should fail.
+ assertFalse(mWificondControl.enableSupplicant());
+ }
+
+ /**
+ * Verifies that disableSupplicant() calls wificond.
+ */
+ @Test
+ public void testDisableSupplicant() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+ when(clientInterface.disableSupplicant()).thenReturn(true);
+
+ mWificondControl.setupDriverForClientMode();
+ assertTrue(mWificondControl.disableSupplicant());
+ verify(clientInterface).disableSupplicant();
+ }
+
+ /**
+ * Verifies that disableSupplicant() returns false when there is no configured
+ * client interface.
+ */
+ @Test
+ public void testDisableSupplicantErrorWhenNoClientInterfaceConfigured() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+
+ // Configure client interface.
+ IClientInterface returnedClientInterface = mWificondControl.setupDriverForClientMode();
+ assertEquals(clientInterface, returnedClientInterface);
+
+ // Tear down interfaces.
+ assertTrue(mWificondControl.tearDownInterfaces());
+
+ // Disabling supplicant should fail.
+ assertFalse(mWificondControl.disableSupplicant());
+ }
+
+ /**
+ * Verifies that tearDownInterfaces() calls wificond.
+ */
+ @Test
+ public void testTearDownInterfaces() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ assertTrue(mWificondControl.tearDownInterfaces());
+
+ verify(wificond).tearDownInterfaces();
+ }
+
+ /**
+ * Verifies that tearDownInterfaces() calls unsubscribeScanEvents() when there was
+ * a configured client interface.
+ */
+ @Test
+ public void testTearDownInterfacesRemovesScanEventSubscription() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ assertTrue(mWificondControl.tearDownInterfaces());
+
+ verify(scanner).unsubscribeScanEvents();
+ }
+
+
+ /**
+ * Verifies that tearDownInterfaces() returns false when wificond is not started.
+ */
+ @Test
+ public void testTearDownInterfacesErrorWhenWificondIsNotStarterd() throws Exception {
+ when(mWifiInjector.makeWificond()).thenReturn(null);
+
+ assertFalse(mWificondControl.tearDownInterfaces());
+ }
+
+ /**
+ * Verifies that signalPoll() calls wificond.
+ */
+ @Test
+ public void testSignalPoll() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+
+ mWificondControl.setupDriverForClientMode();
+ mWificondControl.signalPoll();
+ verify(clientInterface).signalPoll();
+ }
+
+ /**
+ * Verifies that signalPoll() returns null when there is no configured client interface.
+ */
+ @Test
+ public void testSignalPollErrorWhenNoClientInterfaceConfigured() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+
+ // Configure client interface.
+ IClientInterface returnedClientInterface = mWificondControl.setupDriverForClientMode();
+ assertEquals(clientInterface, returnedClientInterface);
+
+ // Tear down interfaces.
+ assertTrue(mWificondControl.tearDownInterfaces());
+
+ // Signal poll should fail.
+ assertEquals(null, mWificondControl.signalPoll());
+ }
+
+ /**
+ * Verifies that getTxPacketCounters() calls wificond.
+ */
+ @Test
+ public void testGetTxPacketCounters() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+
+ mWificondControl.setupDriverForClientMode();
+ mWificondControl.getTxPacketCounters();
+ verify(clientInterface).getPacketCounters();
+ }
+
+ /**
+ * Verifies that getTxPacketCounters() returns null when there is no configured client
+ * interface.
+ */
+ @Test
+ public void testGetTxPacketCountersErrorWhenNoClientInterfaceConfigured() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+
+ // Configure client interface.
+ IClientInterface returnedClientInterface = mWificondControl.setupDriverForClientMode();
+ assertEquals(clientInterface, returnedClientInterface);
+
+ // Tear down interfaces.
+ assertTrue(mWificondControl.tearDownInterfaces());
+
+ // Signal poll should fail.
+ assertEquals(null, mWificondControl.getTxPacketCounters());
+ }
+
+ /**
+ * Verifies that getScanResults() returns null when there is no configured client
+ * interface.
+ */
+ @Test
+ public void testGetScanResultsErrorWhenNoClientInterfaceConfigured() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = getMockClientInterface();
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+
+ // Configure client interface.
+ IClientInterface returnedClientInterface = mWificondControl.setupDriverForClientMode();
+ assertEquals(clientInterface, returnedClientInterface);
+
+ // Tear down interfaces.
+ assertTrue(mWificondControl.tearDownInterfaces());
+
+ // getScanResults should fail.
+ assertEquals(0, mWificondControl.getScanResults().size());
+ }
+
+ /**
+ * Verifies that getScanResults() can parse NativeScanResult from wificond correctly,
+ */
+ @Test
+ public void testGetScanResults() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+ assertNotNull(scanner);
+
+ // Mock the returned array of NativeScanResult.
+ NativeScanResult[] mockScanResults = {MOCK_NATIVE_SCAN_RESULT};
+ when(scanner.getScanResults()).thenReturn(mockScanResults);
+
+ ArrayList<ScanDetail> returnedScanResults = mWificondControl.getScanResults();
+ assertEquals(mockScanResults.length, returnedScanResults.size());
+ // Since NativeScanResult is organized differently from ScanResult, this only checks
+ // a few fields.
+ for (int i = 0; i < mockScanResults.length; i++) {
+ assertArrayEquals(mockScanResults[i].ssid,
+ returnedScanResults.get(i).getScanResult().SSID.getBytes());
+ assertEquals(mockScanResults[i].frequency,
+ returnedScanResults.get(i).getScanResult().frequency);
+ assertEquals(mockScanResults[i].tsf,
+ returnedScanResults.get(i).getScanResult().timestamp);
+ }
+ }
+
+ /**
+ * Verifies that Scan() can convert input parameters to SingleScanSettings correctly.
+ */
+ @Test
+ public void testScan() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ when(scanner.scan(any(SingleScanSettings.class))).thenReturn(true);
+
+ assertTrue(mWificondControl.scan(SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET));
+ verify(scanner).scan(argThat(new ScanMatcher(
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET)));
+ }
+
+ /**
+ * Verifies that Scan() can handle null input parameters correctly.
+ */
+ @Test
+ public void testScanNullParameters() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ when(scanner.scan(any(SingleScanSettings.class))).thenReturn(true);
+
+ assertTrue(mWificondControl.scan(null, null));
+ verify(scanner).scan(argThat(new ScanMatcher(null, null)));
+ }
+
+ /**
+ * Verifies that Scan() can handle wificond scan failure.
+ */
+ @Test
+ public void testScanFailure() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ when(scanner.scan(any(SingleScanSettings.class))).thenReturn(false);
+ assertFalse(mWificondControl.scan(SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_SET));
+ verify(scanner).scan(any(SingleScanSettings.class));
+ }
+
+ /**
+ * Verifies that startPnoScan() can convert input parameters to PnoSettings correctly.
+ */
+ @Test
+ public void testStartPnoScan() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ when(scanner.startPnoScan(any(PnoSettings.class))).thenReturn(true);
+ assertTrue(mWificondControl.startPnoScan(TEST_PNO_SETTINGS));
+ verify(scanner).startPnoScan(argThat(new PnoScanMatcher(TEST_PNO_SETTINGS)));
+ }
+
+ /**
+ * Verifies that stopPnoScan() calls underlying wificond.
+ */
+ @Test
+ public void testStopPnoScan() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ when(scanner.stopPnoScan()).thenReturn(true);
+ assertTrue(mWificondControl.stopPnoScan());
+ verify(scanner).stopPnoScan();
+ }
+
+ /**
+ * Verifies that stopPnoScan() can handle wificond failure.
+ */
+ @Test
+ public void testStopPnoScanFailure() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ when(scanner.stopPnoScan()).thenReturn(false);
+ assertFalse(mWificondControl.stopPnoScan());
+ verify(scanner).stopPnoScan();
+ }
+
+ /**
+ * Verifies that WificondControl can invoke WifiMonitor broadcast methods upon scan
+ * reuslt event.
+ */
+ @Test
+ public void testScanResultEvent() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ ArgumentCaptor<IScanEvent> messageCaptor = ArgumentCaptor.forClass(IScanEvent.class);
+ verify(scanner).subscribeScanEvents(messageCaptor.capture());
+ IScanEvent scanEvent = messageCaptor.getValue();
+ assertNotNull(scanEvent);
+ scanEvent.OnScanResultReady();
+
+ verify(mWifiMonitor).broadcastScanResultEvent(any(String.class));
+ }
+
+ /**
+ * Verifies that WificondControl can invoke WifiMonitor broadcast methods upon scan
+ * failed event.
+ */
+ @Test
+ public void testScanFailedEvent() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ ArgumentCaptor<IScanEvent> messageCaptor = ArgumentCaptor.forClass(IScanEvent.class);
+ verify(scanner).subscribeScanEvents(messageCaptor.capture());
+ IScanEvent scanEvent = messageCaptor.getValue();
+ assertNotNull(scanEvent);
+ scanEvent.OnScanFailed();
+
+ verify(mWifiMonitor).broadcastScanFailedEvent(any(String.class));
+ }
+
+ /**
+ * Verifies that WificondControl can invoke WifiMonitor broadcast methods upon pno scan
+ * reuslt event.
+ */
+ @Test
+ public void testPnoScanResultEvent() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ ArgumentCaptor<IPnoScanEvent> messageCaptor = ArgumentCaptor.forClass(IPnoScanEvent.class);
+ verify(scanner).subscribePnoScanEvents(messageCaptor.capture());
+ IPnoScanEvent pnoScanEvent = messageCaptor.getValue();
+ assertNotNull(pnoScanEvent);
+ pnoScanEvent.OnPnoNetworkFound();
+
+ verify(mWifiMonitor).broadcastPnoScanResultEvent(any(String.class));
+ }
+
+ /**
+ * Verifies that abortScan() calls underlying wificond.
+ */
+ @Test
+ public void testAbortScan() throws Exception {
+ IWifiScannerImpl scanner = setupClientInterfaceAndCreateMockWificondScanner();
+
+ mWificondControl.abortScan();
+ verify(scanner).abortScan();
+ }
+
+ /**
+ * Helper method: create a mock IClientInterface which mocks all neccessary operations.
+ * Returns a mock IClientInterface.
+ */
+ private IClientInterface getMockClientInterface() throws Exception {
+ IClientInterface clientInterface = mock(IClientInterface.class);
+ IWifiScannerImpl scanner = mock(IWifiScannerImpl.class);
+
+ when(clientInterface.getWifiScannerImpl()).thenReturn(scanner);
+
+ return clientInterface;
+ }
+
+ /**
+ * Helper method: Setup interface to client mode for mWificondControl.
+ * Returns a mock IWifiScannerImpl.
+ */
+ private IWifiScannerImpl setupClientInterfaceAndCreateMockWificondScanner() throws Exception {
+ IWificond wificond = mock(IWificond.class);
+ IClientInterface clientInterface = mock(IClientInterface.class);
+ IWifiScannerImpl scanner = mock(IWifiScannerImpl.class);
+
+ when(mWifiInjector.makeWificond()).thenReturn(wificond);
+ when(wificond.createClientInterface()).thenReturn(clientInterface);
+ when(clientInterface.getWifiScannerImpl()).thenReturn(scanner);
+ when(clientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
+
+ assertEquals(clientInterface, mWificondControl.setupDriverForClientMode());
+
+ return scanner;
+ }
+
+ // Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it
+ // matches the provided frequency set and ssid set.
+ private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> {
+ private final Set<Integer> mExpectedFreqs;
+ private final Set<String> mExpectedSsids;
+ ScanMatcher(Set<Integer> expectedFreqs, Set<String> expectedSsids) {
+ this.mExpectedFreqs = expectedFreqs;
+ this.mExpectedSsids = expectedSsids;
+ }
+
+ @Override
+ public boolean matches(SingleScanSettings settings) {
+ ArrayList<ChannelSettings> channelSettings = settings.channelSettings;
+ ArrayList<HiddenNetwork> hiddenNetworks = settings.hiddenNetworks;
+ if (mExpectedFreqs != null) {
+ Set<Integer> freqSet = new HashSet<Integer>();
+ for (ChannelSettings channel : channelSettings) {
+ freqSet.add(channel.frequency);
+ }
+ if (!mExpectedFreqs.equals(freqSet)) {
+ return false;
+ }
+ } else {
+ if (channelSettings != null && channelSettings.size() > 0) {
+ return false;
+ }
+ }
+
+ if (mExpectedSsids != null) {
+ Set<String> ssidSet = new HashSet<String>();
+ for (HiddenNetwork network : hiddenNetworks) {
+ ssidSet.add(NativeUtil.encodeSsid(
+ NativeUtil.byteArrayToArrayList(network.ssid)));
+ }
+ if (!mExpectedSsids.equals(ssidSet)) {
+ return false;
+ }
+
+ } else {
+ if (hiddenNetworks != null && hiddenNetworks.size() > 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "ScanMatcher{mExpectedFreqs=" + mExpectedFreqs
+ + ", mExpectedSsids=" + mExpectedSsids + '}';
+ }
+ }
+
+ // Create a ArgumentMatcher which captures a PnoSettings parameter and checks if it
+ // matches the WifiNative.PnoSettings;
+ private class PnoScanMatcher implements ArgumentMatcher<PnoSettings> {
+ private final WifiNative.PnoSettings mExpectedPnoSettings;
+ PnoScanMatcher(WifiNative.PnoSettings expectedPnoSettings) {
+ this.mExpectedPnoSettings = expectedPnoSettings;
+ }
+ @Override
+ public boolean matches(PnoSettings settings) {
+ if (mExpectedPnoSettings == null) {
+ return false;
+ }
+ if (settings.intervalMs != mExpectedPnoSettings.periodInMs
+ || settings.min2gRssi != mExpectedPnoSettings.min24GHzRssi
+ || settings.min5gRssi != mExpectedPnoSettings.min5GHzRssi) {
+ return false;
+ }
+ if (settings.pnoNetworks == null || mExpectedPnoSettings.networkList == null) {
+ return false;
+ }
+ if (settings.pnoNetworks.size() != mExpectedPnoSettings.networkList.length) {
+ return false;
+ }
+
+ for (int i = 0; i < settings.pnoNetworks.size(); i++) {
+ if (!mExpectedPnoSettings.networkList[i].ssid.equals(NativeUtil.encodeSsid(
+ NativeUtil.byteArrayToArrayList(settings.pnoNetworks.get(i).ssid)))) {
+ return false;
+ }
+ boolean isNetworkHidden = (mExpectedPnoSettings.networkList[i].flags
+ & WifiScanner.PnoSettings.PnoNetwork.FLAG_DIRECTED_SCAN) != 0;
+ if (isNetworkHidden != settings.pnoNetworks.get(i).isHidden) {
+ return false;
+ }
+
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "PnoScanMatcher{" + "mExpectedPnoSettings=" + mExpectedPnoSettings + '}';
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
new file mode 100644
index 0000000..e7a383c
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java
@@ -0,0 +1,823 @@
+/*
+ * 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.aware;
+
+import static android.hardware.wifi.V1_0.NanDataPathChannelCfg.REQUEST_CHANNEL_SETUP;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyShort;
+import static org.mockito.ArgumentMatchers.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.NetworkCapabilities;
+import android.net.NetworkFactory;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.net.wifi.aware.AttachCallback;
+import android.net.wifi.aware.ConfigRequest;
+import android.net.wifi.aware.DiscoverySession;
+import android.net.wifi.aware.DiscoverySessionCallback;
+import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
+import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.IWifiAwareManager;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.PublishDiscoverySession;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.SubscribeDiscoverySession;
+import android.net.wifi.aware.WifiAwareManager;
+import android.net.wifi.aware.WifiAwareNetworkSpecifier;
+import android.net.wifi.aware.WifiAwareSession;
+import android.os.Handler;
+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.Field;
+import java.util.Arrays;
+
+/**
+ * Unit test harness for WifiAwareDataPathStateManager class.
+ */
+@SmallTest
+public class WifiAwareDataPathStateManagerTest {
+ private static final String sAwareInterfacePrefix = "aware_data";
+
+ private TestLooper mMockLooper;
+ private Handler mMockLooperHandler;
+ private WifiAwareStateManager mDut;
+ @Mock private WifiAwareNativeApi mMockNative;
+ @Mock private Context mMockContext;
+ @Mock private IWifiAwareManager mMockAwareService;
+ @Mock private ConnectivityManager mMockCm;
+ @Mock private INetworkManagementService mMockNwMgt;
+ @Mock private WifiAwareDataPathStateManager.NetworkInterfaceWrapper mMockNetworkInterface;
+ @Mock private IWifiAwareEventCallback mMockCallback;
+ @Mock IWifiAwareDiscoverySessionCallback 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();
+ mMockLooperHandler = new Handler(mMockLooper.getLooper());
+
+ mDut = new WifiAwareStateManager();
+ mDut.setNative(mMockNative);
+ mDut.start(mMockContext, mMockLooper.getLooper());
+ mDut.startLate();
+
+ when(mMockNative.getCapabilities(anyShort())).thenReturn(true);
+ when(mMockNative.enableAndConfigure(anyShort(), any(), anyBoolean(),
+ anyBoolean())).thenReturn(true);
+ when(mMockNative.disable(anyShort())).thenReturn(true);
+ when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(true);
+ when(mMockNative.subscribe(anyShort(), anyInt(), any()))
+ .thenReturn(true);
+ when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ any(), anyInt())).thenReturn(true);
+ when(mMockNative.stopPublish(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.stopSubscribe(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.createAwareNetworkInterface(anyShort(), any())).thenReturn(true);
+ when(mMockNative.deleteAwareNetworkInterface(anyShort(), any())).thenReturn(true);
+ when(mMockNative.initiateDataPath(anyShort(), anyInt(), anyInt(), anyInt(),
+ any(), any(), any(), any(),
+ any())).thenReturn(true);
+ when(mMockNative.respondToDataPathRequest(anyShort(), anyBoolean(), anyInt(), any(),
+ any(), any(), any())).thenReturn(true);
+ when(mMockNative.endDataPath(anyShort(), anyInt())).thenReturn(true);
+
+ when(mMockNetworkInterface.configureAgentProperties(any(), any(), anyInt(), any(), any(),
+ any())).thenReturn(true);
+
+ 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;
+
+ Capabilities capabilities = new 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.queryCapabilities();
+ 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).createAwareNetworkInterface(transactionId.capture(),
+ interfaceName.capture());
+ collector.checkThat("interface created -- " + i, sAwareInterfacePrefix + 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).deleteAwareNetworkInterface(transactionId.capture(),
+ interfaceName.capture());
+ int interfaceIndex = Integer.valueOf(
+ interfaceName.getValue().substring(sAwareInterfacePrefix.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).deleteAwareNetworkInterface(transactionId.capture(),
+ interfaceName.capture());
+ collector.checkThat("interface delete pre-create -- " + i,
+ sAwareInterfacePrefix + i, equalTo(interfaceName.getValue()));
+ mDut.onDeleteDataPathInterfaceResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+ }
+ inOrder.verify(mMockNative).createAwareNetworkInterface(transactionId.capture(),
+ interfaceName.capture());
+ collector.checkThat("interface created -- " + i, sAwareInterfacePrefix + 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, false);
+ }
+
+ /**
+ * 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, false);
+ }
+
+ /**
+ * 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, false);
+ }
+
+ /**
+ * 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, false);
+ }
+
+ /**
+ * 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, false);
+ }
+
+ /**
+ * 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, false);
+ }
+
+ /**
+ * Validate the fail flow of the Initiator: use a session network specifier with a non-null
+ * token, but get an immediate failure
+ */
+ @Test
+ public void testDataPathInitiatorNoConfirmationHalFail() throws Exception {
+ testDataPathInitiatorUtility(false, true, true, true, true);
+ }
+
+ /**
+ * Validate the fail flow of a mis-configured request: Publisher as Initiator
+ */
+ @Test
+ public void testDataPathInitiatorOnPublisherError() throws Exception {
+ testDataPathInitiatorResponderMismatchUtility(true);
+ }
+
+ /*
+ * 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 {
+ testDataPathResponderUtility(false, true, true, false);
+ }
+
+ /**
+ * Validate the fail flow of a mis-configured request: Subscriber as Responder
+ */
+ @Test
+ public void testDataPathResponderOnSubscriberError() throws Exception {
+ testDataPathInitiatorResponderMismatchUtility(false);
+ }
+
+ /*
+ * Utilities
+ */
+
+ private void testDataPathInitiatorResponderMismatchUtility(boolean doPublish) throws Exception {
+ final int clientId = 123;
+ final int pubSubId = 11234;
+ final int ndpId = 2;
+ final byte[] pmk = "some bytes".getBytes();
+ final PeerHandle peerHandle = new PeerHandle(1341234);
+ final byte[] peerDiscoveryMac = HexEncoding.decode("000102030405".toCharArray(), false);
+
+ InOrder inOrder = inOrder(mMockNative, mMockCm, mMockCallback, mMockSessionCallback);
+
+ // (0) initialize
+ Pair<Integer, Messenger> res = initDataPathEndPoint(clientId, pubSubId, peerHandle,
+ peerDiscoveryMac, inOrder, doPublish);
+
+ // (1) request network
+ NetworkRequest nr = getSessionNetworkRequest(clientId, res.first, peerHandle, pmk,
+ doPublish);
+
+ // corrupt the network specifier: reverse the role (so it's mis-matched)
+ WifiAwareNetworkSpecifier ns =
+ (WifiAwareNetworkSpecifier) nr.networkCapabilities.getNetworkSpecifier();
+ ns = new WifiAwareNetworkSpecifier(
+ ns.type,
+ 1 - ns.role, // corruption hack
+ ns.clientId,
+ ns.sessionId,
+ ns.peerId,
+ ns.peerMac,
+ ns.pmk,
+ ns.passphrase
+ );
+ nr.networkCapabilities.setNetworkSpecifier(ns);
+
+ Message reqNetworkMsg = Message.obtain();
+ reqNetworkMsg.what = NetworkFactory.CMD_REQUEST_NETWORK;
+ reqNetworkMsg.obj = nr;
+ reqNetworkMsg.arg1 = 0;
+ res.second.send(reqNetworkMsg);
+ mMockLooper.dispatchAll();
+
+ // consequences of failure:
+ // Responder (publisher): responds with a rejection to any data-path requests
+ // Initiator (subscribe): doesn't initiate (i.e. no HAL requests)
+ if (doPublish) {
+ // (2) get request & respond
+ mDut.onDataPathRequestNotification(pubSubId, peerDiscoveryMac, ndpId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).respondToDataPathRequest(anyShort(), eq(false),
+ eq(ndpId), eq(""), eq(null), eq(null), any());
+ }
+
+ verifyNoMoreInteractions(mMockNative, mMockCm);
+ }
+
+ private void testDataPathInitiatorUtility(boolean useDirect, boolean provideMac,
+ boolean providePmk, boolean getConfirmation, boolean immediateHalFailure)
+ throws Exception {
+ final int clientId = 123;
+ final int pubSubId = 11234;
+ final PeerHandle peerHandle = new PeerHandle(1341234);
+ final int ndpId = 2;
+ final byte[] pmk = "some bytes".getBytes();
+ 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);
+
+ if (immediateHalFailure) {
+ when(mMockNative.initiateDataPath(anyShort(), anyInt(), anyInt(), anyInt(), any(),
+ any(), any(), any(), any())).thenReturn(false);
+
+ }
+
+ // (0) initialize
+ Pair<Integer, Messenger> res = initDataPathEndPoint(clientId, pubSubId, peerHandle,
+ peerDiscoveryMac, inOrder, false);
+
+ // (1) request network
+ NetworkRequest nr;
+ if (useDirect) {
+ nr = getDirectNetworkRequest(clientId,
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR,
+ provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null);
+ } else {
+ nr = getSessionNetworkRequest(clientId, res.first, provideMac ? peerHandle : null,
+ providePmk ? pmk : null, false);
+ }
+
+ 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 : peerHandle.peerId),
+ eq(REQUEST_CHANNEL_SETUP), eq(2437), eq(peerDiscoveryMac),
+ eq(sAwareInterfacePrefix + "0"), eq(pmk), eq(null), any());
+ if (immediateHalFailure) {
+ // short-circuit the rest of this test
+ verifyNoMoreInteractions(mMockNative, mMockCm);
+ return;
+ }
+
+ mDut.onInitiateDataPathResponseSuccess(transactionId.getValue(), ndpId);
+ mMockLooper.dispatchAll();
+
+ // (2) get confirmation OR timeout
+ if (getConfirmation) {
+ mDut.onDataPathConfirmNotification(ndpId, peerDataPathMac, true, 0,
+ peerToken.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(), any(), any(),
+ any(), anyInt(), any());
+ } else {
+ assertTrue(mAlarmManager.dispatch(
+ WifiAwareStateManager.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 providePmk, boolean getConfirmation) throws Exception {
+ final int clientId = 123;
+ final int pubSubId = 11234;
+ final PeerHandle peerHandle = new PeerHandle(1341234);
+ final int ndpId = 2;
+ final byte[] pmk = "some bytes".getBytes();
+ 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, peerHandle,
+ peerDiscoveryMac, inOrder, true);
+
+ // (1) request network
+ NetworkRequest nr;
+ if (useDirect) {
+ nr = getDirectNetworkRequest(clientId,
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+ provideMac ? peerDiscoveryMac : null, providePmk ? pmk : null);
+ } else {
+ nr = getSessionNetworkRequest(clientId, res.first, provideMac ? peerHandle : null,
+ providePmk ? pmk : null, true);
+ }
+
+ 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);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).respondToDataPathRequest(transactionId.capture(), eq(true),
+ eq(ndpId), eq(sAwareInterfacePrefix + "0"), eq(providePmk ? pmk : null), eq(null),
+ any());
+ mDut.onRespondToDataPathSetupRequestResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+
+ // (3) get confirmation OR timeout
+ if (getConfirmation) {
+ mDut.onDataPathConfirmNotification(ndpId, peerDataPathMac, true, 0,
+ peerToken.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockCm).registerNetworkAgent(messengerCaptor.capture(), any(), any(),
+ any(), anyInt(), any());
+ } else {
+ assertTrue(mAlarmManager.dispatch(
+ WifiAwareStateManager.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 void installDataPathStateManagerMocks() throws Exception {
+ Field field = WifiAwareStateManager.class.getDeclaredField("mDataPathMgr");
+ field.setAccessible(true);
+ Object mDataPathMgr = field.get(mDut);
+
+ field = WifiAwareDataPathStateManager.class.getDeclaredField("mNwService");
+ field.setAccessible(true);
+ field.set(mDataPathMgr, mMockNwMgt);
+
+ field = WifiAwareDataPathStateManager.class.getDeclaredField("mNiWrapper");
+ field.setAccessible(true);
+ field.set(mDataPathMgr, mMockNetworkInterface);
+ }
+
+ private NetworkRequest getSessionNetworkRequest(int clientId, int sessionId,
+ PeerHandle peerHandle, byte[] pmk, boolean doPublish)
+ throws Exception {
+ final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mMockAwareService);
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+ final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+ ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<DiscoverySession> discoverySession = ArgumentCaptor
+ .forClass(DiscoverySession.class);
+
+ AttachCallback mockCallback = mock(AttachCallback.class);
+ DiscoverySessionCallback mockSessionCallback = mock(
+ DiscoverySessionCallback.class);
+
+ mgr.attach(mMockLooperHandler, configRequest, mockCallback, null);
+ verify(mMockAwareService).connect(any(), any(),
+ clientProxyCallback.capture(), eq(configRequest), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ verify(mockCallback).onAttached(sessionCaptor.capture());
+ if (doPublish) {
+ sessionCaptor.getValue().publish(publishConfig, mockSessionCallback,
+ mMockLooperHandler);
+ verify(mMockAwareService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+ } else {
+ sessionCaptor.getValue().subscribe(subscribeConfig, mockSessionCallback,
+ mMockLooperHandler);
+ verify(mMockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+ sessionProxyCallback.capture());
+ }
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ if (doPublish) {
+ verify(mockSessionCallback).onPublishStarted(
+ (PublishDiscoverySession) discoverySession.capture());
+ } else {
+ verify(mockSessionCallback).onSubscribeStarted(
+ (SubscribeDiscoverySession) discoverySession.capture());
+ }
+
+ NetworkSpecifier ns;
+ if (pmk == null) {
+ ns = discoverySession.getValue().createNetworkSpecifierOpen(peerHandle);
+ } else {
+ ns = discoverySession.getValue().createNetworkSpecifierPmk(peerHandle, pmk);
+ }
+
+ NetworkCapabilities nc = new NetworkCapabilities();
+ nc.clearAll();
+ nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE);
+ 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,
+ byte[] pmk) throws Exception {
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final WifiAwareManager mgr = new WifiAwareManager(mMockContext, mMockAwareService);
+
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+
+ AttachCallback mockCallback = mock(AttachCallback.class);
+
+ mgr.attach(mMockLooperHandler, configRequest, mockCallback, null);
+ verify(mMockAwareService).connect(any(), any(),
+ clientProxyCallback.capture(), eq(configRequest), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ verify(mockCallback).onAttached(sessionCaptor.capture());
+
+ NetworkSpecifier ns;
+ if (pmk == null) {
+ ns = sessionCaptor.getValue().createNetworkSpecifierOpen(role, peer);
+ } else {
+ ns = sessionCaptor.getValue().createNetworkSpecifierPmk(role, peer, pmk);
+ }
+ NetworkCapabilities nc = new NetworkCapabilities();
+ nc.clearAll();
+ nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE);
+ 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,
+ PeerHandle peerHandle, byte[] peerDiscoveryMac, InOrder inOrder,
+ boolean doPublish)
+ throws Exception {
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.android.somePackage";
+ final String someMsg = "some arbitrary message from peer";
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+ final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ Capabilities capabilities = new 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_AWARE_FACTORY", equalTo(strCaptor.getValue()));
+
+ // (1) get capabilities
+ mDut.queryCapabilities();
+ 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).createAwareNetworkInterface(transactionId.capture(),
+ strCaptor.capture());
+ collector.checkThat("interface created -- 0", sAwareInterfacePrefix + 0,
+ equalTo(strCaptor.getValue()));
+ mDut.onCreateDataPathInterfaceResponse(transactionId.getValue(), true, 0);
+ mMockLooper.dispatchAll();
+
+ // (3) create client & session & rx message
+ mDut.connect(clientId, uid, pid, callingPackage, mMockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockCallback).onConnectSuccess(clientId);
+ if (doPublish) {
+ mDut.publish(clientId, publishConfig, mMockSessionCallback);
+ } else {
+ mDut.subscribe(clientId, subscribeConfig, mMockSessionCallback);
+ }
+ mMockLooper.dispatchAll();
+ if (doPublish) {
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ } else {
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0),
+ eq(subscribeConfig));
+ }
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), doPublish, pubSubId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockSessionCallback).onSessionStarted(sessionId.capture());
+ mDut.onMessageReceivedNotification(pubSubId, peerHandle.peerId, peerDiscoveryMac,
+ someMsg.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockSessionCallback).onMessageReceived(peerHandle.peerId,
+ someMsg.getBytes());
+
+ return new Pair<>(sessionId.getValue(), messengerCaptor.getValue());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java
new file mode 100644
index 0000000..50a2e0d
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareNativeManagerTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.aware;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.hardware.wifi.V1_0.IWifiNanIface;
+import android.hardware.wifi.V1_0.IfaceType;
+import android.hardware.wifi.V1_0.WifiStatus;
+import android.hardware.wifi.V1_0.WifiStatusCode;
+
+import com.android.server.wifi.HalDeviceManager;
+
+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 WifiAwareNativeManager.
+ */
+public class WifiAwareNativeManagerTest {
+ private WifiAwareNativeManager mDut;
+ @Mock private WifiAwareStateManager mWifiAwareStateManagerMock;
+ @Mock private HalDeviceManager mHalDeviceManager;
+ @Mock private WifiAwareNativeCallback mWifiAwareNativeCallback;
+ @Mock private IWifiNanIface mWifiNanIfaceMock;
+ private ArgumentCaptor<HalDeviceManager.ManagerStatusListener> mManagerStatusListenerCaptor =
+ ArgumentCaptor.forClass(HalDeviceManager.ManagerStatusListener.class);
+ private ArgumentCaptor<HalDeviceManager.InterfaceDestroyedListener>
+ mDestroyedListenerCaptor = ArgumentCaptor.forClass(
+ HalDeviceManager.InterfaceDestroyedListener.class);
+ private ArgumentCaptor<HalDeviceManager.InterfaceAvailableForRequestListener>
+ mAvailListenerCaptor = ArgumentCaptor.forClass(
+ HalDeviceManager.InterfaceAvailableForRequestListener.class);
+ private InOrder mInOrder;
+ @Rule public ErrorCollector collector = new ErrorCollector();
+
+ private WifiStatus mStatusOk;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mStatusOk = new WifiStatus();
+ mStatusOk.code = WifiStatusCode.SUCCESS;
+
+ when(mWifiNanIfaceMock.registerEventCallback(any())).thenReturn(mStatusOk);
+
+ mDut = new WifiAwareNativeManager(mWifiAwareStateManagerMock,
+ mHalDeviceManager, mWifiAwareNativeCallback);
+
+ mInOrder = inOrder(mWifiAwareStateManagerMock, mHalDeviceManager);
+ }
+
+ /**
+ * Test the control flow of the manager:
+ * 1. onStatusChange (ready/started)
+ * 2. null NAN iface
+ * 3. onAvailableForRequest
+ * 4. non-null NAN iface -> enableUsage
+ * 5. onStatusChange (!started) -> disableUsage
+ * 6. onStatusChange (ready/started)
+ * 7. non-null NAN iface -> enableUsage
+ * 8. onDestroyed -> disableUsage
+ * 9. onStatusChange (!started)
+ */
+ @Test
+ public void testControlFlow() {
+ // configure HalDeviceManager as ready/wifi started
+ when(mHalDeviceManager.isReady()).thenReturn(true);
+ when(mHalDeviceManager.isStarted()).thenReturn(true);
+
+ // validate (and capture) that register manage status callback
+ mInOrder.verify(mHalDeviceManager).registerStatusListener(
+ mManagerStatusListenerCaptor.capture(), any());
+
+ // 1 & 2 onStatusChange (ready/started): validate that trying to get a NAN interface
+ // (make sure gets a NULL)
+ when(mHalDeviceManager.createNanIface(any(), any())).thenReturn(null);
+
+ mManagerStatusListenerCaptor.getValue().onStatusChanged();
+ mInOrder.verify(mHalDeviceManager).registerInterfaceAvailableForRequestListener(
+ eq(IfaceType.NAN), mAvailListenerCaptor.capture(), any());
+ mAvailListenerCaptor.getValue().onAvailableForRequest();
+
+ mInOrder.verify(mHalDeviceManager).createNanIface(
+ mDestroyedListenerCaptor.capture(), any());
+ collector.checkThat("null interface", mDut.getWifiNanIface(), nullValue());
+
+ // 3 & 4 onAvailableForRequest + non-null return value: validate that enables usage
+ when(mHalDeviceManager.createNanIface(any(), any())).thenReturn(mWifiNanIfaceMock);
+
+ mAvailListenerCaptor.getValue().onAvailableForRequest();
+
+ mInOrder.verify(mHalDeviceManager).createNanIface(
+ mDestroyedListenerCaptor.capture(), any());
+ mInOrder.verify(mWifiAwareStateManagerMock).enableUsage();
+ collector.checkThat("non-null interface", mDut.getWifiNanIface(),
+ equalTo(mWifiNanIfaceMock));
+
+ // 5 onStatusChange (!started): disable usage
+ when(mHalDeviceManager.isStarted()).thenReturn(false);
+ mManagerStatusListenerCaptor.getValue().onStatusChanged();
+
+ mInOrder.verify(mWifiAwareStateManagerMock).disableUsage();
+ collector.checkThat("null interface", mDut.getWifiNanIface(), nullValue());
+
+ // 6 & 7 onStatusChange (ready/started) + non-null NAN interface: enable usage
+ when(mHalDeviceManager.isStarted()).thenReturn(true);
+ mManagerStatusListenerCaptor.getValue().onStatusChanged();
+
+ mManagerStatusListenerCaptor.getValue().onStatusChanged();
+ mInOrder.verify(mHalDeviceManager).registerInterfaceAvailableForRequestListener(
+ eq(IfaceType.NAN), mAvailListenerCaptor.capture(), any());
+ mAvailListenerCaptor.getValue().onAvailableForRequest();
+
+ mInOrder.verify(mHalDeviceManager).createNanIface(
+ mDestroyedListenerCaptor.capture(), any());
+ mInOrder.verify(mWifiAwareStateManagerMock).enableUsage();
+ collector.checkThat("non-null interface", mDut.getWifiNanIface(),
+ equalTo(mWifiNanIfaceMock));
+
+ // 8 onDestroyed: disable usage
+ mDestroyedListenerCaptor.getValue().onDestroyed();
+
+ mInOrder.verify(mWifiAwareStateManagerMock).disableUsage();
+ collector.checkThat("null interface", mDut.getWifiNanIface(), nullValue());
+
+ // 9 onStatusChange (!started): nothing more happens
+ when(mHalDeviceManager.isStarted()).thenReturn(false);
+ mManagerStatusListenerCaptor.getValue().onStatusChanged();
+
+ verifyNoMoreInteractions(mWifiAwareStateManagerMock);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareRttStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareRttStateManagerTest.java
new file mode 100644
index 0000000..616e68c
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareRttStateManagerTest.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.aware;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.mockito.ArgumentMatchers.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.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.util.test.BidirectionalAsyncChannelServer;
+
+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 WifiAwareManager class.
+ */
+@SmallTest
+public class WifiAwareRttStateManagerTest {
+ private WifiAwareRttStateManager 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 WifiAwareRttStateManager();
+ 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;
+ WifiAwareClientState mockClient = mock(WifiAwareClientState.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/aware/WifiAwareServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
new file mode 100644
index 0000000..b5e12d2
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java
@@ -0,0 +1,662 @@
+/*
+ * 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.aware;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+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.aware.Characteristics;
+import android.net.wifi.aware.ConfigRequest;
+import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
+import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.SubscribeConfig;
+import android.os.HandlerThread;
+import android.os.IBinder;
+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.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Field;
+
+/**
+ * Unit test harness for WifiAwareStateManager.
+ */
+@SmallTest
+public class WifiAwareServiceImplTest {
+ private static final int MAX_LENGTH = 255;
+
+ private WifiAwareServiceImplSpy mDut;
+ private int mDefaultUid = 1500;
+
+ @Mock
+ private Context mContextMock;
+ @Mock
+ private HandlerThread mHandlerThreadMock;
+ @Mock
+ private PackageManager mPackageManagerMock;
+ @Mock
+ private WifiAwareStateManager mAwareStateManagerMock;
+ @Mock
+ private IBinder mBinderMock;
+ @Mock
+ private IWifiAwareEventCallback mCallbackMock;
+ @Mock
+ private IWifiAwareDiscoverySessionCallback mSessionCallbackMock;
+
+ private HandlerThread mHandlerThread;
+
+ /**
+ * Using instead of spy to avoid native crash failures - possibly due to
+ * spy's copying of state.
+ */
+ private class WifiAwareServiceImplSpy extends WifiAwareServiceImpl {
+ public int fakeUid;
+
+ WifiAwareServiceImplSpy(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_AWARE))
+ .thenReturn(true);
+ when(mAwareStateManagerMock.getCharacteristics()).thenReturn(getCharacteristics());
+
+ mDut = new WifiAwareServiceImplSpy(mContextMock);
+ mDut.fakeUid = mDefaultUid;
+ mDut.start(mHandlerThreadMock, mAwareStateManagerMock);
+ verify(mAwareStateManagerMock).start(eq(mContextMock), any());
+ }
+
+ /**
+ * Validate isUsageEnabled() function
+ */
+ @Test
+ public void testIsUsageEnabled() {
+ mDut.isUsageEnabled();
+
+ verify(mAwareStateManagerMock).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();
+ String callingPackage = "com.google.somePackage";
+
+ mDut.connect(mBinderMock, callingPackage, mCallbackMock,
+ configRequest, false);
+
+ verify(mAwareStateManagerMock).connect(anyInt(), anyInt(), anyInt(),
+ eq(callingPackage), eq(mCallbackMock), eq(configRequest), eq(false));
+ }
+
+ /**
+ * Validate disconnect() - correct pass-through args.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testDisconnect() throws Exception {
+ int clientId = doConnect();
+
+ mDut.disconnect(clientId, mBinderMock);
+
+ verify(mAwareStateManagerMock).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(mAwareStateManagerMock).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(mAwareStateManagerMock).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(mAwareStateManagerMock).disconnect(clientId);
+ validateInternalStateCleanedUp(clientId);
+ }
+
+ /**
+ * Validates that sequential connect() calls return increasing client IDs.
+ */
+ @Test
+ public void testClientIdIncrementing() {
+ int loopCount = 100;
+
+ InOrder inOrder = inOrder(mAwareStateManagerMock);
+ ArgumentCaptor<Integer> clientIdCaptor = ArgumentCaptor.forClass(Integer.class);
+
+ int prevId = 0;
+ for (int i = 0; i < loopCount; ++i) {
+ mDut.connect(mBinderMock, "", mCallbackMock, null, false);
+ inOrder.verify(mAwareStateManagerMock).connect(clientIdCaptor.capture(), anyInt(),
+ anyInt(), any(), eq(mCallbackMock), any(), eq(false));
+ int id = clientIdCaptor.getValue();
+ 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(mAwareStateManagerMock).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();
+ IWifiAwareDiscoverySessionCallback mockCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+
+ mDut.publish(clientId, publishConfig, mockCallback);
+
+ verify(mAwareStateManagerMock).publish(clientId, publishConfig, mockCallback);
+ }
+
+ /**
+ * Validate that publish() verifies the input PublishConfig and fails on an invalid service
+ * name.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishInvalidServiceName() {
+ doBadPublishConfiguration("Including invalid characters - spaces", null, null);
+ }
+
+ /**
+ * Validate that publish() verifies the input PublishConfig and fails on a "very long"
+ * service name.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishServiceNameTooLong() {
+ byte[] byteArray = new byte[MAX_LENGTH + 1];
+ for (int i = 0; i < MAX_LENGTH + 1; ++i) {
+ byteArray[i] = 'a';
+ }
+ doBadPublishConfiguration(new String(byteArray), null, null);
+ }
+
+ /**
+ * Validate that publish() verifies the input PublishConfig and fails on a "very long" ssi.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishSsiTooLong() {
+ doBadPublishConfiguration("correctservicename", new byte[MAX_LENGTH + 1], null);
+ }
+
+ /**
+ * Validate that publish() verifies the input PublishConfig and fails on a "very long" match
+ * filter.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishMatchFilterTooLong() {
+ doBadPublishConfiguration("correctservicename", null, new byte[MAX_LENGTH + 1]);
+ }
+
+ /**
+ * Validate that publish() verifies the input PublishConfig and fails on a bad match filter -
+ * invalid LV.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishMatchFilterBadLv() {
+ byte[] badLv = { 0, 1, 127, 2, 126, 125, 3 };
+ doBadPublishConfiguration("correctservicename", null, badLv);
+ }
+
+ /**
+ * 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(mAwareStateManagerMock).updatePublish(clientId, sessionId, publishConfig);
+ }
+
+ /**
+ * Validate updatePublish() error checking.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdatePublishInvalid() {
+ int sessionId = 1232;
+ PublishConfig publishConfig = new PublishConfig.Builder()
+ .setServiceName("something with spaces").build();
+ int clientId = doConnect();
+
+ mDut.updatePublish(clientId, sessionId, publishConfig);
+
+ verify(mAwareStateManagerMock).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();
+ IWifiAwareDiscoverySessionCallback mockCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+
+ mDut.subscribe(clientId, subscribeConfig, mockCallback);
+
+ verify(mAwareStateManagerMock).subscribe(clientId, subscribeConfig, mockCallback);
+ }
+
+ /**
+ * Validate that subscribe() verifies the input SubscribeConfig and fails on an invalid service
+ * name.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeInvalidServiceName() {
+ doBadSubscribeConfiguration("Including invalid characters - spaces", null, null);
+ }
+
+ /**
+ * Validate that subscribe() verifies the input SubscribeConfig and fails on a "very long"
+ * service name.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeServiceNameTooLong() {
+ byte[] byteArray = new byte[MAX_LENGTH + 1];
+ for (int i = 0; i < MAX_LENGTH + 1; ++i) {
+ byteArray[i] = 'a';
+ }
+ doBadSubscribeConfiguration(new String(byteArray), null, null);
+ }
+
+ /**
+ * Validate that subscribe() verifies the input SubscribeConfig and fails on a "very long" ssi.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeSsiTooLong() {
+ doBadSubscribeConfiguration("correctservicename", new byte[MAX_LENGTH + 1], null);
+ }
+
+ /**
+ * Validate that subscribe() verifies the input SubscribeConfig and fails on a "very long" match
+ * filter.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeMatchFilterTooLong() {
+ doBadSubscribeConfiguration("correctservicename", null, new byte[MAX_LENGTH + 1]);
+ }
+
+ /**
+ * Validate that subscribe() verifies the input SubscribeConfig and fails on a bad match filter
+ * - invalid LV.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeMatchFilterBadLv() {
+ byte[] badLv = { 0, 1, 127, 2, 126, 125, 3 };
+ doBadSubscribeConfiguration("correctservicename", null, badLv);
+ }
+
+ /**
+ * Validate updateSubscribe() error checking.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testUpdateSubscribeInvalid() {
+ int sessionId = 1232;
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder()
+ .setServiceName("something.valid")
+ .setServiceSpecificInfo(new byte[MAX_LENGTH + 1]).build();
+ int clientId = doConnect();
+
+ mDut.updateSubscribe(clientId, sessionId, subscribeConfig);
+
+ verify(mAwareStateManagerMock).updateSubscribe(clientId, sessionId, subscribeConfig);
+ }
+
+ /**
+ * Validate updateSubscribe() validates configuration.
+ */
+ @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(mAwareStateManagerMock).updateSubscribe(clientId, sessionId, subscribeConfig);
+ }
+
+ /**
+ * Validate sendMessage() - correct pass-through args.
+ */
+ @Test
+ public void testSendMessage() {
+ int sessionId = 2394;
+ int peerId = 2032;
+ byte[] message = new byte[MAX_LENGTH];
+ int messageId = 2043;
+ int clientId = doConnect();
+
+ mDut.sendMessage(clientId, sessionId, peerId, message, messageId, 0);
+
+ verify(mAwareStateManagerMock).sendMessage(clientId, sessionId, peerId, message, messageId,
+ 0);
+ }
+
+ /**
+ * Validate sendMessage() validates that message length is correct.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testSendMessageTooLong() {
+ int sessionId = 2394;
+ int peerId = 2032;
+ byte[] message = new byte[MAX_LENGTH + 1];
+ int messageId = 2043;
+ int clientId = doConnect();
+
+ mDut.sendMessage(clientId, sessionId, peerId, message, messageId, 0);
+
+ verify(mAwareStateManagerMock).sendMessage(clientId, sessionId, peerId, message, 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(mAwareStateManagerMock).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 WifiAwareServiceImpl: 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 void doBadPublishConfiguration(String serviceName, byte[] ssi, byte[] matchFilter)
+ throws IllegalArgumentException {
+ // using the hidden constructor since may be passing invalid parameters which would be
+ // caught by the Builder. Want to test whether service side will catch invalidly
+ // constructed configs.
+ PublishConfig publishConfig = new PublishConfig(serviceName.getBytes(), ssi, matchFilter,
+ PublishConfig.PUBLISH_TYPE_UNSOLICITED, 0, true);
+ int clientId = doConnect();
+ IWifiAwareDiscoverySessionCallback mockCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+
+ mDut.publish(clientId, publishConfig, mockCallback);
+
+ verify(mAwareStateManagerMock).publish(clientId, publishConfig, mockCallback);
+ }
+
+ private void doBadSubscribeConfiguration(String serviceName, byte[] ssi, byte[] matchFilter)
+ throws IllegalArgumentException {
+ // using the hidden constructor since may be passing invalid parameters which would be
+ // caught by the Builder. Want to test whether service side will catch invalidly
+ // constructed configs.
+ SubscribeConfig subscribeConfig = new SubscribeConfig(serviceName.getBytes(), ssi,
+ matchFilter, SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE, 0, true);
+ int clientId = doConnect();
+ IWifiAwareDiscoverySessionCallback mockCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+
+ mDut.subscribe(clientId, subscribeConfig, mockCallback);
+
+ verify(mAwareStateManagerMock).subscribe(clientId, subscribeConfig, mockCallback);
+ }
+
+ private int doConnect() {
+ String callingPackage = "com.google.somePackage";
+
+ mDut.connect(mBinderMock, callingPackage, mCallbackMock, null, false);
+
+ ArgumentCaptor<Integer> clientId = ArgumentCaptor.forClass(Integer.class);
+ verify(mAwareStateManagerMock).connect(clientId.capture(), anyInt(), anyInt(),
+ eq(callingPackage), eq(mCallbackMock), eq(new ConfigRequest.Builder().build()),
+ eq(false));
+
+ return clientId.getValue();
+ }
+
+ private static Characteristics getCharacteristics() {
+ Capabilities cap = new Capabilities();
+ cap.maxConcurrentAwareClusters = 1;
+ cap.maxPublishes = 2;
+ cap.maxSubscribes = 2;
+ cap.maxServiceNameLen = MAX_LENGTH;
+ cap.maxMatchFilterLen = MAX_LENGTH;
+ cap.maxTotalMatchFilterLen = 255;
+ cap.maxServiceSpecificInfoLen = MAX_LENGTH;
+ cap.maxExtendedServiceSpecificInfoLen = MAX_LENGTH;
+ cap.maxNdiInterfaces = 1;
+ cap.maxNdpSessions = 1;
+ cap.maxAppInfoLen = 255;
+ cap.maxQueuedTransmitMessages = 6;
+ return cap.toPublicCharacteristics();
+ }
+
+ private int getInternalStateUid(int clientId) throws Exception {
+ Field field = WifiAwareServiceImpl.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 = WifiAwareServiceImpl.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/aware/WifiAwareStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
new file mode 100644
index 0000000..2797c0e
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java
@@ -0,0 +1,2843 @@
+/*
+ * 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.aware;
+
+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.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyShort;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+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.when;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.app.test.MockAnswerUtil;
+import android.app.test.TestAlarmManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.hardware.wifi.V1_0.NanStatusType;
+import android.net.ConnectivityManager;
+import android.net.wifi.RttManager;
+import android.net.wifi.aware.ConfigRequest;
+import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
+import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.WifiAwareManager;
+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 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.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+/**
+ * Unit test harness for WifiAwareStateManager.
+ */
+@SmallTest
+public class WifiAwareStateManagerTest {
+ private TestLooper mMockLooper;
+ private Random mRandomNg = new Random(15687);
+ private WifiAwareStateManager mDut;
+ @Mock private WifiAwareNativeApi mMockNative;
+ @Mock private Context mMockContext;
+ @Mock private AppOpsManager mMockAppOpsManager;
+ @Mock private WifiAwareRttStateManager mMockAwareRttStateManager;
+ TestAlarmManager mAlarmManager;
+ @Mock private WifiAwareDataPathStateManager mMockAwareDataPathStatemanager;
+
+ @Rule
+ public ErrorCollector collector = new ErrorCollector();
+
+ private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
+
+ /**
+ * Pre-test configuration. Initialize and install 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(
+ mock(ConnectivityManager.class));
+ when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mMockAppOpsManager);
+ when(mMockContext.checkPermission(eq(android.Manifest.permission.ACCESS_FINE_LOCATION),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_FINE_LOCATION), anyInt(),
+ any())).thenReturn(AppOpsManager.MODE_ERRORED);
+ when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_COARSE_LOCATION), anyInt(),
+ any())).thenReturn(AppOpsManager.MODE_ERRORED);
+
+ mMockLooper = new TestLooper();
+
+ mDut = new WifiAwareStateManager();
+ mDut.setNative(mMockNative);
+ mDut.start(mMockContext, mMockLooper.getLooper());
+ installMocksInStateManager(mDut, mMockAwareRttStateManager, mMockAwareDataPathStatemanager);
+
+ when(mMockNative.enableAndConfigure(anyShort(), any(), anyBoolean(),
+ anyBoolean())).thenReturn(true);
+ when(mMockNative.disable(anyShort())).thenReturn(true);
+ when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(true);
+ when(mMockNative.subscribe(anyShort(), anyInt(), any()))
+ .thenReturn(true);
+ when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ any(), anyInt())).thenReturn(true);
+ when(mMockNative.stopPublish(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.stopSubscribe(anyShort(), anyInt())).thenReturn(true);
+ when(mMockNative.getCapabilities(anyShort())).thenReturn(true);
+ }
+
+ /**
+ * Validate that Aware data-path interfaces are brought up and down correctly.
+ */
+ @Test
+ public void testAwareDataPathInterfaceUpDown() throws Exception {
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mMockContext, mMockNative, mMockAwareDataPathStatemanager);
+
+ // (1) enable usage
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ validateCorrectAwareStatusChangeBroadcast(inOrder, true);
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockAwareDataPathStatemanager).createAllInterfaces();
+ collector.checkThat("usage enabled", mDut.isUsageEnabled(), equalTo(true));
+
+ // (2) disable usage
+ mDut.disableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockAwareDataPathStatemanager).onAwareDownCleanupDataPaths();
+ inOrder.verify(mMockNative).disable((short) 0);
+ validateCorrectAwareStatusChangeBroadcast(inOrder, false);
+ inOrder.verify(mMockAwareDataPathStatemanager).deleteAllInterfaces();
+ collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
+
+ verifyNoMoreInteractions(mMockNative, mMockAwareDataPathStatemanager);
+ }
+
+ /**
+ * 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 int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+
+ // (1) check initial state
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ validateCorrectAwareStatusChangeBroadcast(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);
+ validateCorrectAwareStatusChangeBroadcast(inOrder, false);
+
+ // (3) try connecting and validate that get nothing (app should be aware of non-availability
+ // through state change broadcast and/or query API)
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ 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 int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
+
+ // (1) check initial state
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ validateCorrectAwareStatusChangeBroadcast(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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (3) disable usage & verify callbacks
+ mDut.disableUsage();
+ mMockLooper.dispatchAll();
+ collector.checkThat("usage disabled", mDut.isUsageEnabled(), equalTo(false));
+ inOrder.verify(mMockNative).disable((short) 0);
+ validateCorrectAwareStatusChangeBroadcast(inOrder, false);
+ validateInternalClientInfoCleanedUp(clientId);
+
+ // (4) try connecting again and validate that just get an onAwareDown
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ 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));
+ validateCorrectAwareStatusChangeBroadcast(inOrder, true);
+
+ // (7) connect (should be successful)
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ verifyNoMoreInteractions(mMockNative, mockCallback);
+ }
+
+ /**
+ * Validates that a HAL failure on enable and configure results in failed callback.
+ */
+ @Test
+ public void testHalFailureEnableAndConfigure() throws Exception {
+ final int clientId = 12341;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mMockContext, mMockNative, mockCallback);
+
+ when(mMockNative.enableAndConfigure(anyShort(), any(), anyBoolean(),
+ anyBoolean())).thenReturn(false);
+
+ // (1) check initial state
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ validateCorrectAwareStatusChangeBroadcast(inOrder, true);
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (2) connect with HAL failure
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ inOrder.verify(mockCallback).onConnectFail(NanStatusType.INTERNAL_FAILURE);
+
+ validateInternalClientInfoCleanedUp(clientId);
+ verifyNoMoreInteractions(mMockNative, mockCallback);
+ }
+
+ /**
+ * Validates that all events are delivered with correct arguments. Validates
+ * that IdentityChanged not delivered if configuration disables delivery.
+ */
+ @Test
+ public void testAwareEventsDelivery() 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 pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int reason = NanStatusType.INTERNAL_FAILURE;
+ final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
+ final byte[] someMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
+ .setClusterHigh(clusterHigh).setMasterPreference(masterPref)
+ .build();
+
+ IWifiAwareEventCallback mockCallback1 = mock(IWifiAwareEventCallback.class);
+ IWifiAwareEventCallback mockCallback2 = mock(IWifiAwareEventCallback.class);
+ ArgumentCaptor<Short> transactionIdCapture = ArgumentCaptor.forClass(Short.class);
+ InOrder inOrder = inOrder(mockCallback1, mockCallback2, mMockNative);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionIdCapture.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionIdCapture.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect 1st and 2nd clients
+ mDut.connect(clientId1, uid, pid, callingPackage, mockCallback1, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
+ eq(configRequest), eq(false), eq(true));
+ short transactionId = transactionIdCapture.getValue();
+ mDut.onConfigSuccessResponse(transactionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback1).onConnectSuccess(clientId1);
+
+ mDut.connect(clientId2, uid, pid, callingPackage, mockCallback2, configRequest, true);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionIdCapture.capture(),
+ eq(configRequest), eq(true), eq(false));
+ transactionId = transactionIdCapture.getValue();
+ mDut.onConfigSuccessResponse(transactionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback2).onConnectSuccess(clientId2);
+
+ // (2) deliver Aware events - without LOCATIONING permission
+ mDut.onClusterChangeNotification(WifiAwareClientState.CLUSTER_CHANGE_EVENT_STARTED,
+ someMac);
+ mDut.onInterfaceAddressChangeNotification(someMac);
+ mMockLooper.dispatchAll();
+
+ inOrder.verify(mockCallback2).onIdentityChanged(ALL_ZERO_MAC);
+
+ // (3) deliver new identity - still without LOCATIONING permission (should get an event)
+ mDut.onInterfaceAddressChangeNotification(someMac2);
+ mMockLooper.dispatchAll();
+
+ inOrder.verify(mockCallback2).onIdentityChanged(ALL_ZERO_MAC);
+
+ // (4) deliver same identity - still without LOCATIONING permission (should
+ // not get an event)
+ mDut.onInterfaceAddressChangeNotification(someMac2);
+ mMockLooper.dispatchAll();
+
+ // (5) deliver new identity - with LOCATIONING permission
+ when(mMockContext.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION),
+ anyInt(), anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mMockAppOpsManager.noteOp(eq(AppOpsManager.OP_COARSE_LOCATION), anyInt(),
+ any())).thenReturn(AppOpsManager.MODE_ALLOWED);
+ mDut.onInterfaceAddressChangeNotification(someMac);
+ mMockLooper.dispatchAll();
+
+ inOrder.verify(mockCallback2).onIdentityChanged(someMac);
+
+ // (6) Aware down (no feedback)
+ mDut.onAwareDownNotification(reason);
+ mMockLooper.dispatchAll();
+
+ validateInternalClientInfoCleanedUp(clientId1);
+ validateInternalClientInfoCleanedUp(clientId2);
+
+ 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 testHalNoResponseTimeout() throws Exception {
+ final int clientId = 12341;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.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 (successfully)
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (2) publish + timeout
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(anyShort(), eq(0), eq(publishConfig));
+ assertTrue(mAlarmManager.dispatch(WifiAwareStateManager.HAL_COMMAND_TIMEOUT_TAG));
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
+ validateInternalNoSessions(clientId);
+
+ // (3) publish + success
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+ mDut.onSessionConfigSuccessResponse(transactionId.getValue(), true, 9999);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(anyInt());
+
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
+ }
+
+ /**
+ * Validates publish flow: (1) initial publish (2) fail informed by notification, (3) fail due
+ * to immediate HAL failure. Expected: get a failure callback.
+ */
+ @Test
+ public void testPublishFail() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int reasonFail = NanStatusType.INTERNAL_FAILURE;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (1) initial publish
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+
+ // (2) publish failure callback (i.e. firmware tried and failed)
+ mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+ validateInternalNoSessions(clientId);
+
+ // (3) publish and get immediate failure (i.e. HAL failed)
+ when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(false);
+
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+ validateInternalNoSessions(clientId);
+
+ 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 testPublishSuccessTerminated() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int reasonTerminate = NanStatusType.SUCCESS;
+ final int publishId = 15;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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();
+
+ // (5) app terminates session
+ mDut.terminateSession(clientId, sessionId.getValue());
+ mMockLooper.dispatchAll();
+
+ // (6) app updates session (app already knows that terminated - will get
+ // a local FAIL).
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
+ mMockLooper.dispatchAll();
+
+ validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue());
+
+ verifyNoMoreInteractions(mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validate the publish flow: (1) initial publish + (2) success + (3) update + (4) update
+ * fails (callback from firmware) + (5) update + (6). Expected: session is still alive after
+ * update failure so second update succeeds (no callbacks) + (7) update + immediate failure from
+ * HAL.
+ */
+ @Test
+ public void testPublishUpdateFail() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int publishId = 15;
+ final int reasonFail = NanStatusType.INTERNAL_FAILURE;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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) update publish
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
+ eq(publishConfig));
+
+ // (4) update fails
+ mDut.onSessionConfigFailResponse(transactionId.getValue(), true, reasonFail);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+
+ // (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();
+
+ // (7) another update + immediate failure
+ when(mMockNative.publish(anyShort(), anyInt(), any())).thenReturn(false);
+
+ mDut.updatePublish(clientId, sessionId.getValue(), publishConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(publishId),
+ eq(publishConfig));
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+
+ 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 testDisconnectWhilePublishPending() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int publishId = 15;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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 (callback from firmware), (3) fail
+ * due to immeidate HAL failure. Expected: get a failure callback.
+ */
+ @Test
+ public void testSubscribeFail() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int reasonFail = NanStatusType.INTERNAL_FAILURE;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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);
+
+ // (3) subscribe and get immediate failure (i.e. HAL failed)
+ when(mMockNative.subscribe(anyShort(), anyInt(), any()))
+ .thenReturn(false);
+
+ mDut.subscribe(clientId, subscribeConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeConfig));
+
+ 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 pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int reasonTerminate = NanStatusType.SUCCESS;
+ final int subscribeId = 15;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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();
+
+ // (5) app terminates session
+ mDut.terminateSession(clientId, sessionId.getValue());
+ mMockLooper.dispatchAll();
+
+ // (6) app updates session
+ mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
+ mMockLooper.dispatchAll();
+
+ validateInternalSessionInfoCleanedUp(clientId, sessionId.getValue());
+
+ verifyNoMoreInteractions(mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validate the subscribe flow: (1) initial subscribe + (2) success + (3) update + (4) update
+ * fails (callback from firmware) + (5) update + (6). Expected: session is still alive after
+ * update failure so second update succeeds (no callbacks). + (7) update + immediate failure
+ * from HAL.
+ */
+ @Test
+ public void testSubscribeUpdateFail() throws Exception {
+ final int clientId = 2005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int subscribeId = 15;
+ final int reasonFail = NanStatusType.INTERNAL_FAILURE;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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();
+
+ // (7) another update + immediate failure
+ when(mMockNative.subscribe(anyShort(), anyInt(), any()))
+ .thenReturn(false);
+
+ mDut.updateSubscribe(clientId, sessionId.getValue(), subscribeConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId),
+ eq(subscribeConfig));
+ inOrder.verify(mockSessionCallback).onSessionConfigFail(reasonFail);
+
+ 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 pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int subscribeId = 15;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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 clientId = 1005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final String serviceName = "some-service-name";
+ final String ssi = "some much longer and more arbitrary data";
+ final int reasonFail = NanStatusType.INTERNAL_FAILURE;
+ final int subscribeId = 15;
+ final 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 String peerMsg = "some message from peer";
+ final int messageId = 6948;
+ final int messageId2 = 6949;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(ssi.getBytes())
+ .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
+ .build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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(),
+ peerMatchFilter.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerMatchFilter.getBytes());
+
+ // (3) message Rx
+ mDut.onMessageReceivedNotification(subscribeId, requestorId, peerMac, peerMsg.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageReceived(requestorId, peerMsg.getBytes());
+
+ // (4) message Tx successful queuing
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
+ short tid1 = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(tid1);
+ mMockLooper.dispatchAll();
+
+ // (5) message Tx successful queuing
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId2,
+ 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId2));
+ short tid2 = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(tid2);
+ mMockLooper.dispatchAll();
+
+ // (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(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Summary: in a single publish session interact with multiple peers
+ * (different MAC addresses).
+ */
+ @Test
+ public void testMultipleMessageSources() throws Exception {
+ final int clientId = 300;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int clusterLow = 7;
+ final int clusterHigh = 7;
+ final int masterPref = 0;
+ final String serviceName = "some-service-name";
+ final int publishId = 88;
+ final int peerId1 = 568;
+ final int peerId2 = 873;
+ final byte[] peerMac1 = HexEncoding.decode("000102030405".toCharArray(), false);
+ final byte[] peerMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
+ final String msgFromPeer1 = "hey from 000102...";
+ final String msgFromPeer2 = "hey from 0607...";
+ final String msgToPeer1 = "hey there 000102...";
+ final String msgToPeer2 = "hey there 0506...";
+ final int msgToPeerId1 = 546;
+ final int msgToPeerId2 = 9654;
+ final int reason = NanStatusType.INTERNAL_FAILURE;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
+ .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+ .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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());
+ mDut.onMessageReceivedNotification(publishId, peerId2, peerMac2, msgFromPeer2.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId1, msgFromPeer1.getBytes());
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId2, msgFromPeer2.getBytes());
+
+ // (4) sending messages back to same peers: one Tx fails, other succeeds
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId2, msgToPeer2.getBytes(),
+ msgToPeerId2, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId2),
+ eq(peerMac2), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
+ short transactionIdVal = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
+ mDut.onMessageSendSuccessNotification(transactionIdVal);
+
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId1, msgToPeer1.getBytes(),
+ msgToPeerId1, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId1),
+ eq(peerMac1), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
+ transactionIdVal = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(transactionIdVal);
+ mDut.onMessageSendFailNotification(transactionIdVal, reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendFail(msgToPeerId1, reason);
+ validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
+ validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
+
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
+ }
+
+ /**
+ * Summary: interact with a peer which changed its identity (MAC address)
+ * but which keeps its requestor instance ID. Should be transparent.
+ */
+ @Test
+ public void testMessageWhilePeerChangesIdentity() throws Exception {
+ final int clientId = 300;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int clusterLow = 7;
+ final int clusterHigh = 7;
+ final int masterPref = 0;
+ final String serviceName = "some-service-name";
+ final int publishId = 88;
+ final int peerId = 568;
+ final byte[] peerMacOrig = HexEncoding.decode("000102030405".toCharArray(), false);
+ final byte[] peerMacLater = HexEncoding.decode("060708090A0B".toCharArray(), false);
+ final String msgFromPeer1 = "hey from 000102...";
+ final String msgFromPeer2 = "hey from 0607...";
+ final String msgToPeer1 = "hey there 000102...";
+ final String msgToPeer2 = "hey there 0506...";
+ final int msgToPeerId1 = 546;
+ final int msgToPeerId2 = 9654;
+ ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
+ .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+ .setPublishType(PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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 & responded to
+ mDut.onMessageReceivedNotification(publishId, peerId, peerMacOrig, msgFromPeer1.getBytes());
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer1.getBytes(),
+ msgToPeerId1, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer1.getBytes());
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
+ eq(peerMacOrig), eq(msgToPeer1.getBytes()), eq(msgToPeerId1));
+ mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
+ mDut.onMessageSendSuccessNotification(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId1);
+ validateInternalSendMessageQueuesCleanedUp(msgToPeerId1);
+
+ // (4) message received with same peer ID but different MAC
+ mDut.onMessageReceivedNotification(publishId, peerId, peerMacLater,
+ msgFromPeer2.getBytes());
+ mDut.sendMessage(clientId, sessionId.getValue(), peerId, msgToPeer2.getBytes(),
+ msgToPeerId2, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerId, msgFromPeer2.getBytes());
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(publishId), eq(peerId),
+ eq(peerMacLater), eq(msgToPeer2.getBytes()), eq(msgToPeerId2));
+ mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
+ mDut.onMessageSendSuccessNotification(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(msgToPeerId2);
+ validateInternalSendMessageQueuesCleanedUp(msgToPeerId2);
+
+ 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 int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ 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();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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(),
+ peerMatchFilter.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerMatchFilter.getBytes());
+
+ // (3) send message to invalid peer ID
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId + 5, ssi.getBytes(),
+ messageId, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
+ NanStatusType.INTERNAL_FAILURE);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validate that on send message errors are handled correctly: immediate send error, queue fail
+ * error (not queue full), and timeout. Behavior: correct callback is dispatched and a later
+ * firmware notification is ignored. Intersperse with one successfull transmission.
+ */
+ @Test
+ public void testSendMessageErrorsImmediateQueueTimeout() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final String ssi = "some much longer and more arbitrary data";
+ final int subscribeId = 15;
+ final 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();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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(),
+ peerMatchFilter.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerMatchFilter.getBytes());
+
+ // (3) send 2 messages and enqueue successfully
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ messageId, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
+ short transactionId1 = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(transactionId1);
+ mMockLooper.dispatchAll();
+
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ messageId + 1, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 1));
+ short transactionId2 = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(transactionId2);
+ mMockLooper.dispatchAll();
+
+ // (4) send a message and get a queueing failure (not queue full)
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId + 2,
+ 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 2));
+ short transactionId3 = transactionId.getValue();
+ mDut.onMessageSendQueuedFailResponse(transactionId3, NanStatusType.INTERNAL_FAILURE);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendFail(messageId + 2,
+ NanStatusType.INTERNAL_FAILURE);
+ validateInternalSendMessageQueuesCleanedUp(messageId + 2);
+
+ // (5) send a message and get an immediate failure (configure first)
+ when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ any(), anyInt())).thenReturn(false);
+
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId + 3,
+ 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId + 3));
+ short transactionId4 = transactionId.getValue();
+ inOrder.verify(mockSessionCallback).onMessageSendFail(messageId + 3,
+ NanStatusType.INTERNAL_FAILURE);
+ validateInternalSendMessageQueuesCleanedUp(messageId + 3);
+
+ // (6) message send timeout
+ assertTrue(mAlarmManager.dispatch(WifiAwareStateManager.HAL_SEND_MESSAGE_TIMEOUT_TAG));
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
+ NanStatusType.INTERNAL_FAILURE);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ // (7) firmware response (unlikely - but good to check)
+ mDut.onMessageSendSuccessNotification(transactionId1);
+ mDut.onMessageSendSuccessNotification(transactionId2);
+
+ // bogus: these didn't even go to firmware or weren't queued
+ mDut.onMessageSendSuccessNotification(transactionId3);
+ mDut.onMessageSendFailNotification(transactionId4, NanStatusType.INTERNAL_FAILURE);
+ 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 int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ 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();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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(),
+ peerMatchFilter.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerMatchFilter.getBytes());
+
+ // (3) send message and enqueue successfully
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(),
+ messageId, retryCount);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), 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(), NanStatusType.NO_OTA_ACK);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), 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 int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ 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();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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(),
+ peerMatchFilter.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerMatchFilter.getBytes());
+
+ // (3) send message and enqueue successfully
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, ssi.getBytes(), messageId,
+ retryCount);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), 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(), NanStatusType.NO_OTA_ACK);
+ mMockLooper.dispatchAll();
+
+ if (i != retryCount) {
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(messageId));
+ mDut.onMessageSendQueuedSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ }
+ }
+
+ inOrder.verify(mockSessionCallback).onMessageSendFail(messageId,
+ NanStatusType.NO_OTA_ACK);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ /**
+ * Validate that the host-side message queue functions. Tests the perfect case of queue always
+ * succeeds and all messages are received on first attempt.
+ */
+ @Test
+ public void testSendMessageQueueSequence() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final String serviceName = "some-service-name";
+ final int subscribeId = 15;
+ final int requestorId = 22;
+ final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
+ final int messageIdBase = 6948;
+ final int numberOfMessages = 30;
+ final int queueDepth = 6;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+ .build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> messageIdCaptor = 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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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, null, null);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, null, null);
+
+ // (3) transmit messages
+ SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
+ null, null, null);
+ when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ any(), anyInt())).thenAnswer(answerObj);
+
+ int remainingMessages = numberOfMessages;
+ for (int i = 0; i < numberOfMessages; ++i) {
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageIdBase + i,
+ 0);
+ mMockLooper.dispatchAll();
+ // at 1/2 interval have the system simulate transmitting a queued message over-the-air
+ if (i % 2 == 1) {
+ assertTrue(answerObj.process());
+ remainingMessages--;
+ mMockLooper.dispatchAll();
+ }
+ }
+ for (int i = 0; i < remainingMessages; ++i) {
+ assertTrue(answerObj.process());
+ mMockLooper.dispatchAll();
+ }
+ assertEquals("queue empty", 0, answerObj.queueSize());
+
+ inOrder.verify(mockSessionCallback, times(numberOfMessages)).onMessageSendSuccess(
+ messageIdCaptor.capture());
+ for (int i = 0; i < numberOfMessages; ++i) {
+ assertEquals("message ID: " + i, (long) messageIdBase + i,
+ (long) messageIdCaptor.getAllValues().get(i));
+ }
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback);
+ }
+
+ /**
+ * Validate that the host-side message queue functions. A combination of imperfect conditions:
+ * - Failure to queue: synchronous firmware error
+ * - Failure to queue: asyncronous firmware error
+ * - Failure to transmit: OTA (which will be retried)
+ * - Failure to transmit: other
+ */
+ @Test
+ public void testSendMessageQueueSequenceImperfect() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final String serviceName = "some-service-name";
+ final int subscribeId = 15;
+ final int requestorId = 22;
+ final byte[] peerMac = HexEncoding.decode("060708090A0B".toCharArray(), false);
+ final int messageIdBase = 6948;
+ final int numberOfMessages = 300;
+ final int queueDepth = 6;
+ final int retransmitCount = 3; // not the maximum
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+ .build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<Integer> messageIdCaptor = 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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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, null, null);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, null, null);
+
+ // (3) transmit messages: configure a mix of failures/success
+ Set<Integer> failQueueCommandImmediately = new HashSet<>();
+ Set<Integer> failQueueCommandLater = new HashSet<>();
+ Map<Integer, Integer> numberOfRetries = new HashMap<>();
+
+ int numOfSuccesses = 0;
+ int numOfFailuresInternalFailure = 0;
+ int numOfFailuresNoOta = 0;
+ for (int i = 0; i < numberOfMessages; ++i) {
+ // random results:
+ // - 0-50: success
+ // - 51-60: retransmit value (which will fail for >5)
+ // - 61-70: fail queue later
+ // - 71-80: fail queue immediately
+ // - 81-90: fail retransmit with non-OTA failure
+ int random = mRandomNg.nextInt(90);
+ if (random <= 50) {
+ numberOfRetries.put(messageIdBase + i, 0);
+ numOfSuccesses++;
+ } else if (random <= 60) {
+ numberOfRetries.put(messageIdBase + i, random - 51);
+ if (random - 51 > retransmitCount) {
+ numOfFailuresNoOta++;
+ } else {
+ numOfSuccesses++;
+ }
+ } else if (random <= 70) {
+ failQueueCommandLater.add(messageIdBase + i);
+ numOfFailuresInternalFailure++;
+ } else if (random <= 80) {
+ failQueueCommandImmediately.add(messageIdBase + i);
+ numOfFailuresInternalFailure++;
+ } else {
+ numberOfRetries.put(messageIdBase + i, -1);
+ numOfFailuresInternalFailure++;
+ }
+ }
+
+ Log.v("WifiAwareStateManagerTest",
+ "failQueueCommandImmediately=" + failQueueCommandImmediately
+ + ", failQueueCommandLater=" + failQueueCommandLater + ", numberOfRetries="
+ + numberOfRetries + ", numOfSuccesses=" + numOfSuccesses
+ + ", numOfFailuresInternalFailure=" + numOfFailuresInternalFailure
+ + ", numOfFailuresNoOta=" + numOfFailuresNoOta);
+
+ SendMessageQueueModelAnswer answerObj = new SendMessageQueueModelAnswer(queueDepth,
+ failQueueCommandImmediately, failQueueCommandLater, numberOfRetries);
+ when(mMockNative.sendMessage(anyShort(), anyInt(), anyInt(), any(),
+ any(), anyInt())).thenAnswer(answerObj);
+
+ for (int i = 0; i < numberOfMessages; ++i) {
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageIdBase + i,
+ retransmitCount);
+ mMockLooper.dispatchAll();
+ }
+
+ while (answerObj.queueSize() != 0) {
+ assertTrue(answerObj.process());
+ mMockLooper.dispatchAll();
+ }
+
+ verify(mockSessionCallback, times(numOfSuccesses)).onMessageSendSuccess(anyInt());
+ verify(mockSessionCallback, times(numOfFailuresInternalFailure)).onMessageSendFail(anyInt(),
+ eq(NanStatusType.INTERNAL_FAILURE));
+ verify(mockSessionCallback, times(numOfFailuresNoOta)).onMessageSendFail(anyInt(),
+ eq(NanStatusType.NO_OTA_ACK));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback);
+ }
+
+ /**
+ * Validate that can send empty message successfully: null, byte[0], ""
+ */
+ @Test
+ public void testSendEmptyMessages() throws Exception {
+ final int clientId = 1005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final String serviceName = "some-service-name";
+ final String ssi = "some much longer and more arbitrary data";
+ final int subscribeId = 15;
+ final 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().setServiceName(serviceName)
+ .setServiceSpecificInfo(ssi.getBytes())
+ .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE)
+ .build();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<byte[]> byteArrayCaptor = ArgumentCaptor.forClass(byte[].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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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(),
+ peerMatchFilter.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerMatchFilter.getBytes());
+
+ // (3) message null Tx successful queuing
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, null, messageId, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), isNull(byte[].class), eq(messageId));
+ short tid = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(tid);
+ mMockLooper.dispatchAll();
+
+ // (4) final Tx results (on-air results)
+ mDut.onMessageSendSuccessNotification(tid);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ // (5) message byte[0] Tx successful queuing
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, new byte[0], messageId, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), eq(new byte[0]), eq(messageId));
+ tid = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(tid);
+ mMockLooper.dispatchAll();
+
+ // (6) final Tx results (on-air results)
+ mDut.onMessageSendSuccessNotification(tid);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ // (7) message "" Tx successful queuing
+ mDut.sendMessage(clientId, sessionId.getValue(), requestorId, "".getBytes(), messageId, 0);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
+ eq(requestorId), eq(peerMac), byteArrayCaptor.capture(), eq(messageId));
+ collector.checkThat("Empty message contents", "",
+ equalTo(new String(byteArrayCaptor.getValue())));
+ tid = transactionId.getValue();
+ mDut.onMessageSendQueuedSuccessResponse(tid);
+ mMockLooper.dispatchAll();
+
+ // (8) final Tx results (on-air results)
+ mDut.onMessageSendSuccessNotification(tid);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onMessageSendSuccess(messageId);
+ validateInternalSendMessageQueuesCleanedUp(messageId);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative);
+ }
+
+ private class SendMessageQueueModelAnswer extends MockAnswerUtil.AnswerWithArguments {
+ private final int mMaxQueueDepth;
+
+ // keyed by message ID
+ private final Set<Integer> mFailQueueCommandImmediately; // return a false
+ private final Set<Integer> mFailQueueCommandLater; // return an error != TX_QUEUE_FULL
+
+ // # of times to return NO_OTA_ACK before returning SUCCESS. So a 0 means success on first
+ // try, a very large number means - never succeed (since max retry is 5).
+ // a -1 impiles a non-OTA failure: on first attempt
+ private final Map<Integer, Integer> mRetryLimit;
+
+ private final LinkedList<Short> mQueue = new LinkedList<>(); // transaction ID (tid)
+ private final Map<Short, Integer> mMessageIdsByTid = new HashMap<>(); // tid -> message ID
+ private final Map<Integer, Integer> mTriesUsedByMid = new HashMap<>(); // mid -> # of retx
+
+ SendMessageQueueModelAnswer(int maxQueueDepth, Set<Integer> failQueueCommandImmediately,
+ Set<Integer> failQueueCommandLater, Map<Integer, Integer> numberOfRetries) {
+ mMaxQueueDepth = maxQueueDepth;
+ mFailQueueCommandImmediately = failQueueCommandImmediately;
+ mFailQueueCommandLater = failQueueCommandLater;
+ mRetryLimit = numberOfRetries;
+
+ if (mRetryLimit != null) {
+ for (int mid : mRetryLimit.keySet()) {
+ mTriesUsedByMid.put(mid, 0);
+ }
+ }
+ }
+
+ public boolean answer(short transactionId, int pubSubId, int requestorInstanceId,
+ byte[] dest, byte[] message, int messageId) throws Exception {
+ if (mFailQueueCommandImmediately != null && mFailQueueCommandImmediately.contains(
+ messageId)) {
+ return false;
+ }
+
+ if (mFailQueueCommandLater != null && mFailQueueCommandLater.contains(messageId)) {
+ mDut.onMessageSendQueuedFailResponse(transactionId, NanStatusType.INTERNAL_FAILURE);
+ } else {
+ if (mQueue.size() <= mMaxQueueDepth) {
+ mQueue.addLast(transactionId);
+ mMessageIdsByTid.put(transactionId, messageId);
+ mDut.onMessageSendQueuedSuccessResponse(transactionId);
+ } else {
+ mDut.onMessageSendQueuedFailResponse(transactionId,
+ NanStatusType.FOLLOWUP_TX_QUEUE_FULL);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Processes the first message in the queue: i.e. responds as if sent over-the-air
+ * (successfully or failed)
+ */
+ boolean process() {
+ if (mQueue.size() == 0) {
+ return false;
+ }
+ short tid = mQueue.poll();
+ int mid = mMessageIdsByTid.get(tid);
+
+ if (mRetryLimit != null && mRetryLimit.containsKey(mid)) {
+ int numRetries = mRetryLimit.get(mid);
+ if (numRetries == -1) {
+ mDut.onMessageSendFailNotification(tid, NanStatusType.INTERNAL_FAILURE);
+ } else {
+ int currentRetries = mTriesUsedByMid.get(mid);
+ if (currentRetries > numRetries) {
+ return false; // shouldn't be retrying!?
+ } else if (currentRetries == numRetries) {
+ mDut.onMessageSendSuccessNotification(tid);
+ } else {
+ mDut.onMessageSendFailNotification(tid, NanStatusType.NO_OTA_ACK);
+ }
+ mTriesUsedByMid.put(mid, currentRetries + 1);
+ }
+ } else {
+ mDut.onMessageSendSuccessNotification(tid);
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the number of elements in the queue.
+ */
+ int queueSize() {
+ return mQueue.size();
+ }
+ }
+
+ /**
+ * 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 pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ 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();
+
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<Integer> sessionId = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<WifiAwareClientState> clientCaptor =
+ ArgumentCaptor.forClass(WifiAwareClientState.class);
+ ArgumentCaptor<RttManager.RttParams[]> rttParamsCaptor =
+ ArgumentCaptor.forClass(RttManager.RttParams[].class);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mMockNative,
+ mMockAwareRttStateManager);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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(),
+ peerMatchFilter.getBytes());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSessionStarted(sessionId.capture());
+ inOrder.verify(mockSessionCallback).onMatch(requestorId, peerSsi.getBytes(),
+ peerMatchFilter.getBytes());
+
+ // (3) start ranging: pass along a valid peer ID and an invalid one
+ mDut.startRanging(clientId, sessionId.getValue(), params, rangingId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockAwareRttStateManager).startRanging(eq(rangingId),
+ clientCaptor.capture(), rttParamsCaptor.capture());
+ collector.checkThat("RttParams[0].bssid", "06:07:08:09:0A:0B",
+ equalTo(rttParamsCaptor.getValue()[0].bssid));
+ collector.checkThat("RttParams[1].bssid", "", equalTo(rttParamsCaptor.getValue()[1].bssid));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mMockNative,
+ mMockAwareRttStateManager);
+ }
+
+ /**
+ * 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 clientId1 = 9999;
+ final int clientId2 = 1001;
+ final int clientId3 = 1005;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int masterPref1 = 111;
+ final int masterPref3 = 115;
+ final int dwInterval1Band24 = 2;
+ final int dwInterval3Band24 = 1;
+ final int dwInterval3Band5 = 0;
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ ArgumentCaptor<ConfigRequest> crCapture = ArgumentCaptor.forClass(ConfigRequest.class);
+
+ ConfigRequest configRequest1 = new ConfigRequest.Builder()
+ .setClusterLow(5).setClusterHigh(100)
+ .setMasterPreference(masterPref1)
+ .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwInterval1Band24)
+ .build();
+
+ ConfigRequest configRequest2 = new ConfigRequest.Builder()
+ .setSupport5gBand(true) // compatible
+ .setClusterLow(7).setClusterHigh(155) // incompatible!
+ .setMasterPreference(0) // compatible
+ .build();
+
+ ConfigRequest configRequest3 = new ConfigRequest.Builder()
+ .setSupport5gBand(true) // compatible (will use true)
+ .setClusterLow(5).setClusterHigh(100) // identical (hence compatible)
+ .setMasterPreference(masterPref3) // compatible (will use max)
+ // compatible: will use min
+ .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwInterval3Band24)
+ // compatible: will use interval3 since interval1 not init
+ .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwInterval3Band5)
+ .build();
+
+ IWifiAwareEventCallback mockCallback1 = mock(IWifiAwareEventCallback.class);
+ IWifiAwareEventCallback mockCallback2 = mock(IWifiAwareEventCallback.class);
+ IWifiAwareEventCallback mockCallback3 = mock(IWifiAwareEventCallback.class);
+
+ InOrder inOrder = inOrder(mMockNative, mockCallback1, mockCallback2, mockCallback3);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) config1 (valid)
+ mDut.connect(clientId1, uid, pid, callingPackage, mockCallback1, configRequest1, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ crCapture.capture(), eq(false), eq(true));
+ collector.checkThat("merge: stage 1", crCapture.getValue(), equalTo(configRequest1));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback1).onConnectSuccess(clientId1);
+
+ // (2) config2 (incompatible with config1)
+ mDut.connect(clientId2, uid, pid, callingPackage, mockCallback2, configRequest2, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback2).onConnectFail(NanStatusType.INTERNAL_FAILURE);
+ validateInternalClientInfoCleanedUp(clientId2);
+
+ // (3) config3 (compatible with config1)
+ mDut.connect(clientId3, uid, pid, callingPackage, mockCallback3, configRequest3, true);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ crCapture.capture(), eq(true), eq(false));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback3).onConnectSuccess(clientId3);
+
+ collector.checkThat("support 5g: or", true, equalTo(crCapture.getValue().mSupport5gBand));
+ collector.checkThat("master preference: max", Math.max(masterPref1, masterPref3),
+ equalTo(crCapture.getValue().mMasterPreference));
+ collector.checkThat("dw interval on 2.4: ~min",
+ Math.min(dwInterval1Band24, dwInterval3Band24),
+ equalTo(crCapture.getValue().mDiscoveryWindowInterval[ConfigRequest
+ .NAN_BAND_24GHZ]));
+ collector.checkThat("dw interval on 5: ~min", dwInterval3Band5,
+ equalTo(crCapture.getValue().mDiscoveryWindowInterval[ConfigRequest
+ .NAN_BAND_5GHZ]));
+
+ // (4) disconnect config3: downgrade to config1
+ mDut.disconnect(clientId3);
+ mMockLooper.dispatchAll();
+ validateInternalClientInfoCleanedUp(clientId3);
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ crCapture.capture(), eq(false), eq(false));
+
+ collector.checkThat("configRequest1", configRequest1, equalTo(crCapture.getValue()));
+
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+
+ // (5) disconnect config1: disable
+ mDut.disconnect(clientId1);
+ mMockLooper.dispatchAll();
+ validateInternalClientInfoCleanedUp(clientId1);
+ inOrder.verify(mMockNative).disable((short) 0);
+
+ verifyNoMoreInteractions(mMockNative, mockCallback1, mockCallback2, mockCallback3);
+ }
+
+ /**
+ * Summary: disconnect a client while there are pending transactions.
+ */
+ @Test
+ public void testDisconnectWithPendingTransactions() throws Exception {
+ final int clientId = 125;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int clusterLow = 5;
+ final int clusterHigh = 100;
+ final int masterPref = 111;
+ final String serviceName = "some-service-name";
+ final String ssi = "some much longer and more arbitrary data";
+ final int publishId = 22;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
+ .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ serviceName).setServiceSpecificInfo(ssi.getBytes()).setPublishType(
+ PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (2) publish (no response yet)
+ mDut.publish(clientId, publishConfig, mockSessionCallback);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishConfig));
+
+ // (3) disconnect (but doesn't get executed until get a RESPONSE to the
+ // previous publish)
+ mDut.disconnect(clientId);
+ mMockLooper.dispatchAll();
+
+ // (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();
+
+ // (6) got some callback on original publishId - should be ignored
+ mDut.onSessionTerminatedNotification(publishId, 0, true);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mMockNative, mockCallback, mockSessionCallback);
+ }
+
+ /**
+ * 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 testUnknownTransactionType() throws Exception {
+ final int clientId = 129;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ final int clusterLow = 15;
+ final int clusterHigh = 192;
+ final int masterPref = 234;
+ final String serviceName = "some-service-name";
+ final String ssi = "some much longer and more arbitrary data";
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
+ .setClusterHigh(clusterHigh).setMasterPreference(masterPref).build();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ serviceName).setServiceSpecificInfo(ssi.getBytes()).setPublishType(
+ PublishConfig.PUBLISH_TYPE_UNSOLICITED).build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockPublishSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback, mockPublishSessionCallback);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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 testNoOpTransaction() throws Exception {
+ final int clientId = 1294;
+ final int uid = 1000;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), 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;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ InOrder inOrder = inOrder(mMockNative, mockCallback);
+
+ mDut.enableUsage();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).getCapabilities(transactionId.capture());
+ mDut.onCapabilitiesUpdateResponse(transactionId.getValue(), getCapabilities());
+ mMockLooper.dispatchAll();
+
+ // (1) connect and succeed
+ mDut.connect(clientId, uid, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ short transactionIdConfig = transactionId.getValue();
+ mDut.onConfigSuccessResponse(transactionIdConfig);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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], new byte[0]);
+ mDut.onSessionTerminatedNotification(-1, -1, true);
+ mDut.onSessionTerminatedNotification(-1, -1, false);
+ mDut.onMessageReceivedNotification(-1, -1, new byte[0], new byte[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 pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ 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);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest),
+ eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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(NanStatusType.INTERNAL_FAILURE);
+
+ 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 pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ 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);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ // (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(NanStatusType.INTERNAL_FAILURE);
+
+ 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;
+ final int pid = 2000;
+ final String callingPackage = "com.google.somePackage";
+ int loopCount = 100;
+
+ 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);
+ IWifiAwareEventCallback mockCallback = mock(IWifiAwareEventCallback.class);
+ IWifiAwareDiscoverySessionCallback mockSessionCallback = mock(
+ IWifiAwareDiscoverySessionCallback.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, pid, callingPackage, mockCallback, configRequest, false);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
+ eq(configRequest), eq(false), eq(true));
+ mDut.onConfigSuccessResponse(transactionId.getValue());
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onConnectSuccess(clientId);
+
+ int prevId = 0;
+ for (int i = 0; i < loopCount; ++i) {
+ // (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("Session ID incrementing", sessionId.getValue() > prevId);
+ }
+ prevId = sessionId.getValue();
+ }
+ }
+
+ /*
+ * Tests of internal state of WifiAwareStateManager: 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.
+ */
+
+ /**
+ * 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.
+ *
+ * @param clientId The ID of the client which should be deleted.
+ */
+ private void validateInternalClientInfoCleanedUp(int clientId) throws Exception {
+ WifiAwareClientState 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
+ * (deleted) after a session is terminated through API (not callback!). To
+ * be used in every test which terminates a session.
+ *
+ * @param clientId The ID of the client containing the session.
+ * @param sessionId The ID of the terminated session.
+ */
+ private void validateInternalSessionInfoCleanedUp(int clientId, int sessionId)
+ throws Exception {
+ WifiAwareClientState client = getInternalClientState(mDut, clientId);
+ collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
+ WifiAwareDiscoverySessionState 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 {
+ WifiAwareClientState client = getInternalClientState(mDut, clientId);
+ collector.checkThat("Client record exists clientId=" + clientId, client, notNullValue());
+
+ Field field = WifiAwareClientState.class.getDeclaredField("mSessions");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ SparseArray<WifiAwareDiscoverySessionState> sessions =
+ (SparseArray<WifiAwareDiscoverySessionState>) field.get(client);
+
+ collector.checkThat("No sessions exist for clientId=" + clientId, sessions.size(),
+ equalTo(0));
+ }
+
+ /**
+ * Validates that the broadcast sent on Aware status change is correct.
+ *
+ * @param expectedEnabled The expected change status - i.e. are we expected
+ * to announce that Aware is enabled (true) or disabled (false).
+ */
+ private void validateCorrectAwareStatusChangeBroadcast(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(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED));
+ }
+
+ /*
+ * Utilities
+ */
+ private void dumpDut(String prefix) {
+ StringWriter sw = new StringWriter();
+ mDut.dump(null, new PrintWriter(sw), null);
+ Log.e("WifiAwareStateManagerTest", prefix + sw.toString());
+ }
+
+ private static void installMocksInStateManager(WifiAwareStateManager awareStateManager,
+ WifiAwareRttStateManager mockRtt, WifiAwareDataPathStateManager mockDpMgr)
+ throws Exception {
+ Field field = WifiAwareStateManager.class.getDeclaredField("mRtt");
+ field.setAccessible(true);
+ field.set(awareStateManager, mockRtt);
+
+ field = WifiAwareStateManager.class.getDeclaredField("mDataPathMgr");
+ field.setAccessible(true);
+ field.set(awareStateManager, mockDpMgr);
+ }
+
+ private static WifiAwareClientState getInternalClientState(WifiAwareStateManager dut,
+ int clientId) throws Exception {
+ Field field = WifiAwareStateManager.class.getDeclaredField("mClients");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ SparseArray<WifiAwareClientState> clients = (SparseArray<WifiAwareClientState>) field.get(
+ dut);
+
+ return clients.get(clientId);
+ }
+
+ private static WifiAwareDiscoverySessionState getInternalSessionState(
+ WifiAwareClientState client, int sessionId) throws Exception {
+ Field field = WifiAwareClientState.class.getDeclaredField("mSessions");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ SparseArray<WifiAwareDiscoverySessionState> sessions =
+ (SparseArray<WifiAwareDiscoverySessionState>) field.get(client);
+
+ return sessions.get(sessionId);
+ }
+
+ private void validateInternalSendMessageQueuesCleanedUp(int messageId) throws Exception {
+ Field field = WifiAwareStateManager.class.getDeclaredField("mSm");
+ field.setAccessible(true);
+ WifiAwareStateManager.WifiAwareStateMachine sm =
+ (WifiAwareStateManager.WifiAwareStateMachine) field.get(mDut);
+
+ field = WifiAwareStateManager.WifiAwareStateMachine.class.getDeclaredField(
+ "mHostQueuedSendMessages");
+ field.setAccessible(true);
+ SparseArray<Message> hostQueuedSendMessages = (SparseArray<Message>) field.get(sm);
+
+ field = WifiAwareStateManager.WifiAwareStateMachine.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 Capabilities getCapabilities() {
+ Capabilities cap = new Capabilities();
+ cap.maxConcurrentAwareClusters = 1;
+ cap.maxPublishes = 2;
+ cap.maxSubscribes = 2;
+ cap.maxServiceNameLen = 255;
+ cap.maxMatchFilterLen = 255;
+ cap.maxTotalMatchFilterLen = 255;
+ cap.maxServiceSpecificInfoLen = 255;
+ cap.maxExtendedServiceSpecificInfoLen = 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/hotspot2/ANQPDataTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPDataTest.java
new file mode 100644
index 0000000..b4e667b
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPDataTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.hotspot2;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.ANQPData}.
+ *
+ * TODO(b/33000864): add more test once the ANQP elements cleanup are completed, which will
+ * allow easy construction of ANQP elements for testing.
+ */
+@SmallTest
+public class ANQPDataTest {
+ @Mock Clock mClock;
+
+ /**
+ * Sets up test.
+ */
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ // Returning the initial timestamp.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(0L);
+ }
+
+ /**
+ * Verify creation of ANQPData with null elements.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void createWithNullElements() throws Exception {
+ ANQPData data = new ANQPData(mClock, null);
+ Map<Constants.ANQPElementType, ANQPElement> elements = data.getElements();
+ assertTrue(elements.isEmpty());
+ }
+
+ /**
+ * Verify the data expiration behavior.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyExpiration() throws Exception {
+ ANQPData data = new ANQPData(mClock, null);
+ assertFalse(data.expired(ANQPData.DATA_LIFETIME_MILLISECONDS - 1));
+ assertTrue(data.expired(ANQPData.DATA_LIFETIME_MILLISECONDS));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java
new file mode 100644
index 0000000..5fbd4aa
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPMatcherTest.java
@@ -0,0 +1,338 @@
+/*
+ * 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.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.EAPConstants;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.hotspot2.anqp.CellularNetwork;
+import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmData;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmElement;
+import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement;
+import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
+import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
+import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
+import com.android.server.wifi.hotspot2.anqp.eap.InnerAuthEAP;
+import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.ANQPMatcher}.
+ */
+@SmallTest
+public class ANQPMatcherTest {
+ /**
+ * Verify that domain name match will fail when a null Domain Name ANQP element is provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainNameWithNullElement() throws Exception {
+ assertFalse(ANQPMatcher.matchDomainName(null, "test.com", null, null));
+ }
+
+ /**
+ * Verify that domain name match will succeed when the specified FQDN matches a domain name
+ * in the Domain Name ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainNameUsingFQDN() throws Exception {
+ String fqdn = "test.com";
+ String[] domains = new String[] {fqdn};
+ DomainNameElement element = new DomainNameElement(Arrays.asList(domains));
+ assertTrue(ANQPMatcher.matchDomainName(element, fqdn, null, null));
+ }
+
+ /**
+ * Verify that domain name match will succeed when the specified IMSI parameter and IMSI list
+ * matches a 3GPP network domain in the Domain Name ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainNameUsingIMSI() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123457890", "123498723"});
+ // 3GPP network domain with MCC=123 and MNC=456.
+ String[] domains = new String[] {"wlan.mnc457.mcc123.3gppnetwork.org"};
+ DomainNameElement element = new DomainNameElement(Arrays.asList(domains));
+ assertTrue(ANQPMatcher.matchDomainName(element, null, imsiParam, simImsiList));
+ }
+
+ /**
+ * Verify that roaming consortium match will fail when a null Roaming Consortium ANQP
+ * element is provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchRoamingConsortiumWithNullElement() throws Exception {
+ assertFalse(ANQPMatcher.matchRoamingConsortium(null, new long[0]));
+ }
+
+ /**
+ * Verify that a roaming consortium match will succeed when the specified OI matches
+ * an OI in the Roaming Consortium ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchRoamingConsortium() throws Exception {
+ long oi = 0x1234L;
+ RoamingConsortiumElement element =
+ new RoamingConsortiumElement(Arrays.asList(new Long[] {oi}));
+ assertTrue(ANQPMatcher.matchRoamingConsortium(element, new long[] {oi}));
+ }
+
+ /**
+ * Verify that an indeterminate match will be returned when matching a null NAI Realm
+ * ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithNullElement() throws Exception {
+ assertEquals(AuthMatch.INDETERMINATE, ANQPMatcher.matchNAIRealm(null, "test.com",
+ EAPConstants.EAP_TLS, new InnerAuthEAP(EAPConstants.EAP_TTLS)));
+ }
+
+ /**
+ * Verify that an indeterminate match will be returned when matching a NAI Realm
+ * ANQP element contained no NAI realm data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithEmtpyRealmData() throws Exception {
+ NAIRealmElement element = new NAIRealmElement(new ArrayList<NAIRealmData>());
+ assertEquals(AuthMatch.INDETERMINATE, ANQPMatcher.matchNAIRealm(element, "test.com",
+ EAPConstants.EAP_TLS, null));
+ }
+
+ /**
+ * Verify that a realm match will be returned when the specified realm matches a realm
+ * in the NAI Realm ANQP element with no EAP methods.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithRealmMatch() throws Exception {
+ String realm = "test.com";
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), new ArrayList<EAPMethod>());
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+ assertEquals(AuthMatch.REALM, ANQPMatcher.matchNAIRealm(element, realm,
+ EAPConstants.EAP_TLS, null));
+ }
+
+ /**
+ * Verify that a realm and method match will be returned when the specified realm and EAP
+ * method matches a realm in the NAI Realm ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithRealmMethodMatch() throws Exception {
+ // Test data.
+ String realm = "test.com";
+ int eapMethodID = EAPConstants.EAP_TLS;
+
+ // Setup NAI Realm element.
+ EAPMethod method = new EAPMethod(eapMethodID, new HashMap<Integer, Set<AuthParam>>());
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), Arrays.asList(new EAPMethod[] {method}));
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+
+ assertEquals(AuthMatch.REALM | AuthMatch.METHOD,
+ ANQPMatcher.matchNAIRealm(element, realm, eapMethodID, null));
+ }
+
+ /**
+ * Verify that an exact match will be returned when the specified realm, EAP
+ * method, and the authentication parameter matches a realm with the associated EAP method and
+ * authentication parameter in the NAI Realm ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithExactMatch() throws Exception {
+ // Test data.
+ String realm = "test.com";
+ int eapMethodID = EAPConstants.EAP_TTLS;
+ NonEAPInnerAuth authParam = new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAP);
+ Set<AuthParam> authSet = new HashSet<>();
+ authSet.add(authParam);
+ Map<Integer, Set<AuthParam>> authMap = new HashMap<>();
+ authMap.put(authParam.getAuthTypeID(), authSet);
+
+ // Setup NAI Realm element.
+ EAPMethod method = new EAPMethod(eapMethodID, authMap);
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), Arrays.asList(new EAPMethod[] {method}));
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+
+ assertEquals(AuthMatch.EXACT,
+ ANQPMatcher.matchNAIRealm(element, realm, eapMethodID, authParam));
+ }
+
+ /**
+ * Verify that a mismatch (AuthMatch.NONE) will be returned when the specified EAP method
+ * doesn't match with the corresponding EAP method in the NAI Realm ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithEAPMethodMismatch() throws Exception {
+ // Test data.
+ String realm = "test.com";
+ int eapMethodID = EAPConstants.EAP_TTLS;
+ NonEAPInnerAuth authParam = new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAP);
+ Set<AuthParam> authSet = new HashSet<>();
+ authSet.add(authParam);
+ Map<Integer, Set<AuthParam>> authMap = new HashMap<>();
+ authMap.put(authParam.getAuthTypeID(), authSet);
+
+ // Setup NAI Realm element.
+ EAPMethod method = new EAPMethod(eapMethodID, authMap);
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), Arrays.asList(new EAPMethod[] {method}));
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+
+ assertEquals(AuthMatch.NONE,
+ ANQPMatcher.matchNAIRealm(element, realm, EAPConstants.EAP_TLS, null));
+ }
+
+ /**
+ * Verify that a mismatch (AuthMatch.NONE) will be returned when the specified authentication
+ * parameter doesn't match with the corresponding authentication parameter in the NAI Realm
+ * ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealmWithAuthTypeMismatch() throws Exception {
+ // Test data.
+ String realm = "test.com";
+ int eapMethodID = EAPConstants.EAP_TTLS;
+ NonEAPInnerAuth authParam = new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAP);
+ Set<AuthParam> authSet = new HashSet<>();
+ authSet.add(authParam);
+ Map<Integer, Set<AuthParam>> authMap = new HashMap<>();
+ authMap.put(authParam.getAuthTypeID(), authSet);
+
+ // Setup NAI Realm element.
+ EAPMethod method = new EAPMethod(eapMethodID, authMap);
+ NAIRealmData realmData = new NAIRealmData(
+ Arrays.asList(new String[] {realm}), Arrays.asList(new EAPMethod[] {method}));
+ NAIRealmElement element = new NAIRealmElement(
+ Arrays.asList(new NAIRealmData[] {realmData}));
+
+ // Mismatch in authentication type.
+ assertEquals(AuthMatch.NONE,
+ ANQPMatcher.matchNAIRealm(element, realm, EAPConstants.EAP_TTLS,
+ new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_PAP)));
+ }
+
+ /**
+ * Verify that 3GPP Network match will fail when a null element is provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetworkWithNullElement() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123456789", "123498723"});
+ assertFalse(ANQPMatcher.matchThreeGPPNetwork(null, imsiParam, simImsiList));
+ }
+
+ /**
+ * Verify that 3GPP network will succeed when the given 3GPP Network ANQP element contained
+ * a MCC-MNC that matches the both IMSI parameter and an IMSI from the IMSI list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetwork() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123456789", "123498723"});
+
+ CellularNetwork network = new CellularNetwork(Arrays.asList(new String[] {"123456"}));
+ ThreeGPPNetworkElement element =
+ new ThreeGPPNetworkElement(Arrays.asList(new CellularNetwork[] {network}));
+ // The MCC-MNC provided in 3GPP Network ANQP element matches both IMSI parameter
+ // and an IMSI from the installed SIM card.
+ assertTrue(ANQPMatcher.matchThreeGPPNetwork(element, imsiParam, simImsiList));
+ }
+
+ /**
+ * Verify that 3GPP network will failed when the given 3GPP Network ANQP element contained
+ * a MCC-MNC that match the IMSI parameter but not the IMSI list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetworkWithoutSimImsiMatch() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123457890", "123498723"});
+
+ CellularNetwork network = new CellularNetwork(Arrays.asList(new String[] {"123456"}));
+ ThreeGPPNetworkElement element =
+ new ThreeGPPNetworkElement(Arrays.asList(new CellularNetwork[] {network}));
+ // The MCC-MNC provided in 3GPP Network ANQP element doesn't match any of the IMSIs
+ // from the installed SIM card.
+ assertFalse(ANQPMatcher.matchThreeGPPNetwork(element, imsiParam, simImsiList));
+ }
+
+ /**
+ * Verify that 3GPP network will failed when the given 3GPP Network ANQP element contained
+ * a MCC-MNC that doesn't match with the IMSI parameter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetworkWithImsiParamMismatch() throws Exception {
+ IMSIParameter imsiParam = new IMSIParameter("1234", true);
+ List<String> simImsiList = Arrays.asList(new String[] {"123457890", "123498723"});
+
+ CellularNetwork network = new CellularNetwork(Arrays.asList(new String[] {"123356"}));
+ ThreeGPPNetworkElement element =
+ new ThreeGPPNetworkElement(Arrays.asList(new CellularNetwork[] {network}));
+ // The MCC-MNC provided in 3GPP Network ANQP element doesn't match the IMSI parameter.
+ assertFalse(ANQPMatcher.matchThreeGPPNetwork(element, imsiParam, simImsiList));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPNetworkKeyTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPNetworkKeyTest.java
new file mode 100644
index 0000000..1586d76
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPNetworkKeyTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.ANQPNetworkKey}.
+ */
+@SmallTest
+public class ANQPNetworkKeyTest {
+ private static final String SSID = "TestSSID";
+ private static final long BSSID = 0x123456L;
+ private static final long HESSID = 0x789012L;
+ private static final int ANQP_DOMAIN_ID = 1;
+
+ /**
+ * Verify that building a SSID based key works as expected.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void buildStandardESSKey() throws Exception {
+ ANQPNetworkKey expectedKey = new ANQPNetworkKey(SSID, 0, 0, ANQP_DOMAIN_ID);
+ ANQPNetworkKey actualKey = ANQPNetworkKey.buildKey(SSID, BSSID, 0, ANQP_DOMAIN_ID);
+ assertEquals(expectedKey, actualKey);
+ }
+
+ /**
+ * Verify that building a HESSID based key works as expected.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void buildHessidKey() throws Exception {
+ ANQPNetworkKey expectedKey = new ANQPNetworkKey(null, 0, HESSID, ANQP_DOMAIN_ID);
+ ANQPNetworkKey actualKey = ANQPNetworkKey.buildKey(SSID, BSSID, HESSID, ANQP_DOMAIN_ID);
+ assertEquals(expectedKey, actualKey);
+ }
+
+ /**
+ * Verify that building a key based on an AP (SSID + BSSID) works as expected.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void buildAPKey() throws Exception {
+ ANQPNetworkKey expectedKey = new ANQPNetworkKey(SSID, BSSID, 0, 0);
+ ANQPNetworkKey actualKey = ANQPNetworkKey.buildKey(SSID, BSSID, HESSID, 0);
+ assertEquals(expectedKey, actualKey);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java
new file mode 100644
index 0000000..10c1472
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/ANQPRequestManagerTest.java
@@ -0,0 +1,308 @@
+/*
+ * 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.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.ANQPRequestManager}.
+ */
+@SmallTest
+public class ANQPRequestManagerTest {
+ private static final long TEST_BSSID = 0x123456L;
+ private static final ANQPNetworkKey TEST_ANQP_KEY =
+ new ANQPNetworkKey("TestSSID", TEST_BSSID, 0, 0);
+
+ private static final List<Constants.ANQPElementType> R1_ANQP_WITHOUT_RC = Arrays.asList(
+ Constants.ANQPElementType.ANQPVenueName,
+ Constants.ANQPElementType.ANQPIPAddrAvailability,
+ Constants.ANQPElementType.ANQPNAIRealm,
+ Constants.ANQPElementType.ANQP3GPPNetwork,
+ Constants.ANQPElementType.ANQPDomName);
+
+ private static final List<Constants.ANQPElementType> R1_ANQP_WITH_RC = Arrays.asList(
+ Constants.ANQPElementType.ANQPVenueName,
+ Constants.ANQPElementType.ANQPIPAddrAvailability,
+ Constants.ANQPElementType.ANQPNAIRealm,
+ Constants.ANQPElementType.ANQP3GPPNetwork,
+ Constants.ANQPElementType.ANQPDomName,
+ Constants.ANQPElementType.ANQPRoamingConsortium);
+
+ private static final List<Constants.ANQPElementType> R1R2_ANQP_WITHOUT_RC = Arrays.asList(
+ Constants.ANQPElementType.ANQPVenueName,
+ Constants.ANQPElementType.ANQPIPAddrAvailability,
+ Constants.ANQPElementType.ANQPNAIRealm,
+ Constants.ANQPElementType.ANQP3GPPNetwork,
+ Constants.ANQPElementType.ANQPDomName,
+ Constants.ANQPElementType.HSFriendlyName,
+ Constants.ANQPElementType.HSWANMetrics,
+ Constants.ANQPElementType.HSConnCapability,
+ Constants.ANQPElementType.HSOSUProviders);
+
+ private static final List<Constants.ANQPElementType> R1R2_ANQP_WITH_RC = Arrays.asList(
+ Constants.ANQPElementType.ANQPVenueName,
+ Constants.ANQPElementType.ANQPIPAddrAvailability,
+ Constants.ANQPElementType.ANQPNAIRealm,
+ Constants.ANQPElementType.ANQP3GPPNetwork,
+ Constants.ANQPElementType.ANQPDomName,
+ Constants.ANQPElementType.ANQPRoamingConsortium,
+ Constants.ANQPElementType.HSFriendlyName,
+ Constants.ANQPElementType.HSWANMetrics,
+ Constants.ANQPElementType.HSConnCapability,
+ Constants.ANQPElementType.HSOSUProviders);
+
+ @Mock PasspointEventHandler mHandler;
+ @Mock Clock mClock;
+ ANQPRequestManager mManager;
+
+ /**
+ * Test setup.
+ */
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ mManager = new ANQPRequestManager(mHandler, mClock);
+ }
+
+ /**
+ * Verify that the expected set of ANQP elements are being requested when the targeted AP
+ * doesn't provide roaming consortium OIs and doesn't support Hotspot 2.0 Release 2 ANQP
+ * elements, based on the IEs in the scan result .
+ *
+ * @throws Exception
+ */
+ @Test
+ public void requestR1ANQPElementsWithoutRC() throws Exception {
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ }
+
+ /**
+ * Verify that the expected set of ANQP elements are being requested when the targeted AP
+ * does provide roaming consortium OIs and doesn't support Hotspot 2.0 Release ANQP elements,
+ * based on the IEs in the scan result.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void requestR1ANQPElementsWithRC() throws Exception {
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITH_RC)).thenReturn(true);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, true, false));
+ }
+
+ /**
+ * Verify that the expected set of ANQP elements are being requested when the targeted AP
+ * doesn't provide roaming consortium OIs and does support Hotspot 2.0 Release ANQP elements,
+ * based on the IEs in the scan result.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void requestR1R2ANQPElementsWithoutRC() throws Exception {
+ when(mHandler.requestANQP(TEST_BSSID, R1R2_ANQP_WITHOUT_RC)).thenReturn(true);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, true));
+ }
+
+ /**
+ * Verify that the expected set of ANQP elements are being requested when the targeted AP
+ * does provide roaming consortium OIs and support Hotspot 2.0 Release ANQP elements,
+ * based on the IEs in the scan result.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void requestR1R2ANQPElementsWithRC() throws Exception {
+ when(mHandler.requestANQP(TEST_BSSID, R1R2_ANQP_WITH_RC)).thenReturn(true);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, true, true));
+ }
+
+ /**
+ * Verify that attempt to request ANQP elements from an AP will fail when there is a request
+ * already pending. The request will succeed when the hold off time is up.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void requestANQPElementsWithPendingRequest() throws Exception {
+ // Send the initial request.
+ long startTime = 0;
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ reset(mHandler);
+
+ // Attempt another request will fail while one is still pending and hold off time is not up
+ // yet.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime + 1);
+ assertFalse(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ verify(mHandler, never()).requestANQP(anyLong(), anyObject());
+ reset(mHandler);
+
+ // Attempt other request will succeed after the hold off time is up.
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(startTime + ANQPRequestManager.BASE_HOLDOFF_TIME_MILLISECONDS);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ }
+
+ /**
+ * Verify that an immediate attempt to request ANQP elements from an AP will succeed when
+ * the previous request is failed on sending.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void requestANQPElementsAfterRequestSendFailure() throws Exception {
+ // Initial request failed to send.
+ long startTime = 0;
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(false);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime);
+ assertFalse(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ reset(mHandler);
+
+ // Verify that new request is not being held off after previous send failure.
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ }
+
+ /**
+ * Verify that an immediate attempt to request ANQP elements from an AP will succeed when
+ * the previous request is completed with success.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void requestANQPElementsAfterRequestSucceeded() throws Exception {
+ // Send the initial request.
+ long startTime = 0;
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ reset(mHandler);
+
+ // Request completed with success. Verify that the key associated with the request
+ // is returned.
+ assertEquals(TEST_ANQP_KEY, mManager.onRequestCompleted(TEST_BSSID, true));
+
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime + 1);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ }
+
+ /**
+ * Verify that an immediate attempt to request ANQP elements from an AP will fail when
+ * the previous request is completed with failure. The request will succeed after the
+ * hold off time is up.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void requestANQPElementsAfterRequestFailed() throws Exception {
+ // Send the initial request.
+ long startTime = 0;
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ reset(mHandler);
+
+ // Request completed with failure. Verify that the key associated with the request
+ // is returned
+ assertEquals(TEST_ANQP_KEY, mManager.onRequestCompleted(TEST_BSSID, false));
+
+ // Attempt another request will fail since the hold off time is not up yet.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(startTime + 1);
+ assertFalse(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ verify(mHandler, never()).requestANQP(anyLong(), anyObject());
+
+ // Attempt another request will succeed after the hold off time is up.
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(startTime + ANQPRequestManager.BASE_HOLDOFF_TIME_MILLISECONDS);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ }
+
+ /**
+ * Verify the hold off time for each unanswered query, and that it will stay the same after
+ * reaching the max hold off count {@link ANQPRequestManager#MAX_HOLDOFF_COUNT}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void requestANQPElementsWithMaxRetries() throws Exception {
+ long currentTime = 0;
+
+ // Initial request.
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTime);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ reset(mHandler);
+
+ // Sending the request with the hold off time based on the current hold off count.
+ for (int i = 0; i <= ANQPRequestManager.MAX_HOLDOFF_COUNT; i++) {
+ long currentHoldOffTime = ANQPRequestManager.BASE_HOLDOFF_TIME_MILLISECONDS * (1 << i);
+ currentTime += (currentHoldOffTime - 1);
+
+ // Request will fail before the hold off time is up.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTime);
+ assertFalse(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ verify(mHandler, never()).requestANQP(anyLong(), anyObject());
+
+ // Request will succeed when the hold off time is up.
+ currentTime += 1;
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTime);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ reset(mHandler);
+ }
+
+ // Verify that the hold off time is max out at the maximum hold off count.
+ currentTime += (ANQPRequestManager.BASE_HOLDOFF_TIME_MILLISECONDS
+ * (1 << ANQPRequestManager.MAX_HOLDOFF_COUNT) - 1);
+
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTime);
+ assertFalse(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ verify(mHandler, never()).requestANQP(anyLong(), anyObject());
+
+ currentTime += 1;
+ when(mHandler.requestANQP(TEST_BSSID, R1_ANQP_WITHOUT_RC)).thenReturn(true);
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTime);
+ assertTrue(mManager.requestANQPElements(TEST_BSSID, TEST_ANQP_KEY, false, false));
+ reset(mHandler);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/AnqpCacheTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/AnqpCacheTest.java
new file mode 100644
index 0000000..8c0d982
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/AnqpCacheTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.hotspot2;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.hotspot2.ANQPData;
+import com.android.server.wifi.hotspot2.AnqpCache;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.AnqpCache}.
+ *
+ * TODO(b/33000864): add more test once the ANQP elements cleanup are completed, which will
+ * allow easy construction of ANQP elements for testing.
+ */
+@SmallTest
+public class AnqpCacheTest {
+ private static final ANQPNetworkKey ENTRY_KEY = new ANQPNetworkKey("test", 0L, 0L, 1);
+
+ @Mock Clock mClock;
+ AnqpCache mCache;
+
+ /**
+ * Sets up test.
+ */
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ // Returning the initial timestamp.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(0L);
+ mCache = new AnqpCache(mClock);
+ }
+
+ /**
+ * Verify expectation for addEntry and getEntry.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addAndGetEntry() throws Exception {
+ mCache.addEntry(ENTRY_KEY, null);
+ ANQPData data = mCache.getEntry(ENTRY_KEY);
+ assertNotNull(data);
+ assertTrue(data.getElements().isEmpty());
+ }
+
+ /**
+ * Verify that getting a non-existing entry will return a null.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getNonExistEntry() throws Exception {
+ assertNull(mCache.getEntry(ENTRY_KEY));
+ }
+
+ /**
+ * Verify the expectation for the sweep function (expired entries will be removed).
+ *
+ * @throws Exception
+ */
+ @Test
+ public void sweepRemoveExpiredEntry() throws Exception {
+ mCache.addEntry(ENTRY_KEY, null);
+
+ // Sweep the cache when the entry is not expired.
+ when(mClock.getElapsedSinceBootMillis())
+ .thenReturn(AnqpCache.CACHE_SWEEP_INTERVAL_MILLISECONDS);
+ mCache.sweep();
+ assertNotNull(mCache.getEntry(ENTRY_KEY));
+
+ // Sweep the cache when the entry is expired.
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(ANQPData.DATA_LIFETIME_MILLISECONDS);
+ mCache.sweep();
+ assertNull(mCache.getEntry(ENTRY_KEY));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/DomainMatcherTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/DomainMatcherTest.java
new file mode 100644
index 0000000..c015921
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/DomainMatcherTest.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.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.DomainMatcher}.
+ */
+@SmallTest
+public class DomainMatcherTest {
+ private static final String PRIMARY_DOMAIN = "google.com";
+ private static final String SECONDARY_DOMAIN1 = "android.com";
+ private static final String SECONDARY_DOMAIN2 = "testing.test.com";
+
+ /**
+ * Test data for isSubDomain function.
+ */
+ private static final Map<String, Integer> TEST_DOMAIN_MAP = new HashMap<>();
+ static {
+ TEST_DOMAIN_MAP.put("", DomainMatcher.MATCH_NONE);
+ TEST_DOMAIN_MAP.put("com", DomainMatcher.MATCH_NONE);
+ TEST_DOMAIN_MAP.put("test.com", DomainMatcher.MATCH_NONE);
+ TEST_DOMAIN_MAP.put("google.com", DomainMatcher.MATCH_PRIMARY);
+ TEST_DOMAIN_MAP.put("test.google.com", DomainMatcher.MATCH_PRIMARY);
+ TEST_DOMAIN_MAP.put("android.com", DomainMatcher.MATCH_SECONDARY);
+ TEST_DOMAIN_MAP.put("test.android.com", DomainMatcher.MATCH_SECONDARY);
+ TEST_DOMAIN_MAP.put("testing.test.com", DomainMatcher.MATCH_SECONDARY);
+ TEST_DOMAIN_MAP.put("adbcd.testing.test.com", DomainMatcher.MATCH_SECONDARY);
+ }
+
+ /**
+ * Test data for arg2SubdomainOfArg1 function.
+ */
+ private static final Map<Pair<String, String>, Boolean> TEST_ARG_DOMAIN_MAP = new HashMap<>();
+ static {
+ TEST_ARG_DOMAIN_MAP.put(new Pair<String, String>("test.com", "abc.test.com"), true);
+ TEST_ARG_DOMAIN_MAP.put(new Pair<String, String>("test.com", "ad.abc.test.com"), true);
+ TEST_ARG_DOMAIN_MAP.put(new Pair<String, String>("com", "test.com"), true);
+ TEST_ARG_DOMAIN_MAP.put(new Pair<String, String>("abc.test.com", "test.com"), false);
+ TEST_ARG_DOMAIN_MAP.put(new Pair<String, String>("test1.com", "test.com"), false);
+ TEST_ARG_DOMAIN_MAP.put(new Pair<String, String>("test.com", "com"), false);
+ }
+
+ /**
+ * Verify that creating a matcher with empty domains doesn't cause any exceptions.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void createMatcherWithEmptyDomains() throws Exception {
+ DomainMatcher domainMatcher = new DomainMatcher(null, null);
+ assertEquals(DomainMatcher.MATCH_NONE, domainMatcher.isSubDomain("google.com"));
+ }
+
+ /**
+ * Verify that matching a null domain doesn't cause any exceptions.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNullDomain() throws Exception {
+ DomainMatcher domainMatcher = new DomainMatcher(PRIMARY_DOMAIN,
+ Arrays.asList(SECONDARY_DOMAIN1, SECONDARY_DOMAIN2));
+ assertEquals(DomainMatcher.MATCH_NONE, domainMatcher.isSubDomain(null));
+ }
+
+ /**
+ * Verify the domain matching expectations based on the predefined {@link #TEST_DOMAIN_MAP}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchTestDomains() throws Exception {
+ DomainMatcher domainMatcher = new DomainMatcher(PRIMARY_DOMAIN,
+ Arrays.asList(SECONDARY_DOMAIN1, SECONDARY_DOMAIN2));
+ for (Map.Entry<String, Integer> entry : TEST_DOMAIN_MAP.entrySet()) {
+ assertEquals(entry.getValue().intValue(), domainMatcher.isSubDomain(entry.getKey()));
+ }
+ }
+
+ /**
+ * Verify that the correct match status is returned when a domain matches both primary
+ * and secondary domain (primary domain have precedence over secondary).
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainWithBothPrimaryAndSecondary() throws Exception {
+ DomainMatcher domainMatcher = new DomainMatcher(PRIMARY_DOMAIN,
+ Arrays.asList(PRIMARY_DOMAIN));
+ assertEquals(DomainMatcher.MATCH_PRIMARY, domainMatcher.isSubDomain(PRIMARY_DOMAIN));
+ }
+
+ /**
+ * Verify domain matching expectation when the secondary domain is a sub-domain of the
+ * primary domain.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainWhenSecondaryIsSubdomainOfPrimary() throws Exception {
+ DomainMatcher domainMatcher = new DomainMatcher("google.com",
+ Arrays.asList("test.google.com"));
+ assertEquals(DomainMatcher.MATCH_PRIMARY, domainMatcher.isSubDomain("google.com"));
+ assertEquals(DomainMatcher.MATCH_PRIMARY, domainMatcher.isSubDomain("test.google.com"));
+ assertEquals(DomainMatcher.MATCH_PRIMARY,
+ domainMatcher.isSubDomain("abcd.test.google.com"));
+ }
+
+ /**
+ * Verify domain matching expectations when the secondary domain is a sub-domain of the
+ * primary domain.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainWhenPrimaryIsSubdomainOfSecondary() throws Exception {
+ DomainMatcher domainMatcher = new DomainMatcher("test.google.com",
+ Arrays.asList("google.com"));
+ assertEquals(DomainMatcher.MATCH_SECONDARY, domainMatcher.isSubDomain("google.com"));
+ assertEquals(DomainMatcher.MATCH_SECONDARY, domainMatcher.isSubDomain("test2.google.com"));
+ assertEquals(DomainMatcher.MATCH_PRIMARY, domainMatcher.isSubDomain("test.google.com"));
+ assertEquals(DomainMatcher.MATCH_PRIMARY,
+ domainMatcher.isSubDomain("adcd.test.google.com"));
+ }
+
+ /**
+ * Verify domain matching expectations when the domain names contained empty label (domain
+ * name that contained "..").
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchDomainWithEmptyLabel() throws Exception {
+ DomainMatcher domainMatcher = new DomainMatcher("test.google..com",
+ Arrays.asList("google..com"));
+ assertEquals(DomainMatcher.MATCH_PRIMARY, domainMatcher.isSubDomain("test.google..com"));
+ assertEquals(DomainMatcher.MATCH_SECONDARY, domainMatcher.isSubDomain("google..com"));
+ }
+
+ /**
+ * Verify domain matching expectation for arg2SubdomainOfArg1 based on predefined
+ * {@link #TEST_ARG_DOMAIN_MAP}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyArg2SubdomainOfArg1() throws Exception {
+ for (Map.Entry<Pair<String, String>, Boolean> entry : TEST_ARG_DOMAIN_MAP.entrySet()) {
+ assertEquals(entry.getValue().booleanValue(),
+ DomainMatcher.arg2SubdomainOfArg1(entry.getKey().first, entry.getKey().second));
+ }
+ }
+
+ /**
+ * Verify that arg2SubdomainOfArg1 works as expected when pass in null domains.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void arg2SubdomainOfArg1WithNullDomain() throws Exception {
+ assertFalse(DomainMatcher.arg2SubdomainOfArg1(null, "test.com"));
+ assertFalse(DomainMatcher.arg2SubdomainOfArg1("test.com", null));
+ assertFalse(DomainMatcher.arg2SubdomainOfArg1(null, null));
+ }
+
+ /**
+ * Verify that arg2SubdomainOfArg1 works as expected when domain contains empty label.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void arg2SubdomainOfArg1WithEmptyLabel() throws Exception {
+ assertTrue(DomainMatcher.arg2SubdomainOfArg1("test..com", "adsf.test..com"));
+ }
+
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/LegacyPasspointConfigParserTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/LegacyPasspointConfigParserTest.java
new file mode 100644
index 0000000..10ebceb
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/LegacyPasspointConfigParserTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import static org.junit.Assert.*;
+
+import android.os.FileUtils;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.LegacyPasspointConfigParser}.
+ */
+@SmallTest
+public class LegacyPasspointConfigParserTest {
+ private static final String TEST_CONFIG =
+ "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n"
+ + "8:MgmtTree+\n"
+ + "17:PerProviderSubscription+\n"
+ + "4:r1i1+\n"
+ + "6:HomeSP+\n"
+ + "c:FriendlyName=12:Test Provider 1™\n"
+ + "4:FQDN=9:test1.net\n"
+ + "13:RoamingConsortiumOI=9:1234,5678\n"
+ + ".\n"
+ + "a:Credential+\n"
+ + "10:UsernamePassword+\n"
+ + "8:Username=5:user1\n"
+ + "8:Password=5:pass1\n"
+ + "\n"
+ + "9:EAPMethod+\n"
+ + "7:EAPType=2:21\n"
+ + "b:InnerMethod=3:PAP\n"
+ + ".\n"
+ + ".\n"
+ + "5:Realm=9:test1.com\n"
+ + ".\n"
+ + ".\n"
+ + ".\n"
+ + "17:PerProviderSubscription+\n"
+ + "4:r1i2+\n"
+ + "6:HomeSP+\n"
+ + "c:FriendlyName=f:Test Provider 2\n"
+ + "4:FQDN=9:test2.net\n"
+ + ".\n"
+ + "a:Credential+\n"
+ + "3:SIM+\n"
+ + "4:IMSI=4:1234\n"
+ + "7:EAPType=2:18\n"
+ + ".\n"
+ + "5:Realm=9:test2.com\n"
+ + ".\n"
+ + ".\n"
+ + ".\n"
+ + ".\n";
+
+ /**
+ * Helper function for generating {@link LegacyPasspointConfig} objects based on the predefined
+ * test configuration string {@link #TEST_CONFIG}
+ *
+ * @return Map of FQDN to {@link LegacyPasspointConfig}
+ */
+ private Map<String, LegacyPasspointConfig> generateTestConfig() {
+ Map<String, LegacyPasspointConfig> configs = new HashMap<>();
+
+ LegacyPasspointConfig config1 = new LegacyPasspointConfig();
+ config1.mFqdn = "test1.net";
+ config1.mFriendlyName = "Test Provider 1™";
+ config1.mRoamingConsortiumOis = new long[] {0x1234, 0x5678};
+ config1.mRealm = "test1.com";
+ configs.put("test1.net", config1);
+
+ LegacyPasspointConfig config2 = new LegacyPasspointConfig();
+ config2.mFqdn = "test2.net";
+ config2.mFriendlyName = "Test Provider 2";
+ config2.mRealm = "test2.com";
+ config2.mImsi = "1234";
+ configs.put("test2.net", config2);
+
+ return configs;
+ }
+
+ /**
+ * Helper function for parsing configuration data.
+ *
+ * @param data The configuration data to parse
+ * @return Map of FQDN to {@link LegacyPasspointConfig}
+ * @throws Exception
+ */
+ private Map<String, LegacyPasspointConfig> parseConfig(String data) throws Exception {
+ // Write configuration data to file.
+ File configFile = File.createTempFile("LegacyPasspointConfig", "");
+ FileUtils.stringToFile(configFile, data);
+
+ // Parse the configuration file.
+ LegacyPasspointConfigParser parser = new LegacyPasspointConfigParser();
+ Map<String, LegacyPasspointConfig> configMap =
+ parser.parseConfig(configFile.getAbsolutePath());
+
+ configFile.delete();
+ return configMap;
+ }
+
+ /**
+ * Verify that the expected {@link LegacyPasspointConfig} objects are return when parsing
+ * predefined test configuration data {@link #TEST_CONFIG}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseTestConfig() throws Exception {
+ Map<String, LegacyPasspointConfig> parsedConfig = parseConfig(TEST_CONFIG);
+ assertEquals(generateTestConfig(), parsedConfig);
+ }
+
+ /**
+ * Verify that an empty map is return when parsing a configuration containing an empty
+ * configuration (MgmtTree).
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseEmptyConfig() throws Exception {
+ String emptyConfig = "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n"
+ + "8:MgmtTree+\n"
+ + ".\n";
+ Map<String, LegacyPasspointConfig> parsedConfig = parseConfig(emptyConfig);
+ assertTrue(parsedConfig.isEmpty());
+ }
+
+ /**
+ * Verify that an empty map is return when parsing an empty configuration data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseEmptyData() throws Exception {
+ Map<String, LegacyPasspointConfig> parsedConfig = parseConfig("");
+ assertTrue(parsedConfig.isEmpty());
+ }
+
+ /**
+ * Verify that an IOException is thrown when parsing a configuration containing an unknown
+ * root name. The expected root name is "MgmtTree".
+ *
+ * @throws Exception
+ */
+ @Test(expected = IOException.class)
+ public void parseConfigWithUnknownRootName() throws Exception {
+ String config = "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n"
+ + "8:TestTest+\n"
+ + ".\n";
+ parseConfig(config);
+ }
+
+ /**
+ * Verify that an IOException is thrown when parsing a configuration containing a line with
+ * mismatched string length for the name.
+ *
+ * @throws Exception
+ */
+ @Test(expected = IOException.class)
+ public void parseConfigWithMismatchedStringLengthInName() throws Exception {
+ String config = "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n"
+ + "9:MgmtTree+\n"
+ + ".\n";
+ parseConfig(config);
+ }
+
+ /**
+ * Verify that an IOException is thrown when parsing a configuration containing a line with
+ * mismatched string length for the value.
+ *
+ * @throws Exception
+ */
+ @Test(expected = IOException.class)
+ public void parseConfigWithMismatchedStringLengthInValue() throws Exception {
+ String config = "tree 3:1.2(urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0)\n"
+ + "8:MgmtTree+\n"
+ + "4:test=5:test\n"
+ + ".\n";
+ parseConfig(config);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigStoreDataTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigStoreDataTest.java
new file mode 100644
index 0000000..8e808ef
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointConfigStoreDataTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.net.wifi.hotspot2.pps.Policy;
+import android.net.wifi.hotspot2.pps.UpdateParameter;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.wifi.SIMAccessor;
+import com.android.server.wifi.WifiKeyStore;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointConfigStoreData}.
+ */
+@SmallTest
+public class PasspointConfigStoreDataTest {
+ private static final String TEST_CA_CERTIFICATE_ALIAS = "CaCert";
+ private static final String TEST_CLIENT_CERTIFICATE_ALIAS = "ClientCert";
+ private static final String TEST_CLIENT_PRIVATE_KEY_ALIAS = "ClientPrivateKey";
+ private static final long TEST_PROVIDER_ID = 1;
+ private static final int TEST_CREATOR_UID = 1234;
+
+ @Mock WifiKeyStore mKeyStore;
+ @Mock SIMAccessor mSimAccessor;
+ @Mock PasspointConfigStoreData.DataSource mDataSource;
+ PasspointConfigStoreData mConfigStoreData;
+
+ /** Sets up test. */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mConfigStoreData = new PasspointConfigStoreData(mKeyStore, mSimAccessor, mDataSource);
+ }
+
+ /**
+ * Helper function for generating a {@link PasspointConfiguration} for testing the XML
+ * serialization/deserialization logic.
+ *
+ * @return {@link PasspointConfiguration}
+ * @throws Exception
+ */
+ private PasspointConfiguration createFullPasspointConfiguration() throws Exception {
+ DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ byte[] certFingerprint = new byte[32];
+ Arrays.fill(certFingerprint, (byte) 0x1f);
+
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.setUpdateIdentifier(12);
+ config.setCredentialPriority(99);
+
+ // AAA Server trust root.
+ Map<String, byte[]> trustRootCertList = new HashMap<>();
+ trustRootCertList.put("server1.trust.root.com", certFingerprint);
+ config.setTrustRootCertList(trustRootCertList);
+
+ // Subscription update.
+ UpdateParameter subscriptionUpdate = new UpdateParameter();
+ subscriptionUpdate.setUpdateIntervalInMinutes(120);
+ subscriptionUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_SSP);
+ subscriptionUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER);
+ subscriptionUpdate.setServerUri("subscription.update.com");
+ subscriptionUpdate.setUsername("subscriptionUser");
+ subscriptionUpdate.setBase64EncodedPassword("subscriptionPass");
+ subscriptionUpdate.setTrustRootCertUrl("subscription.update.cert.com");
+ subscriptionUpdate.setTrustRootCertSha256Fingerprint(certFingerprint);
+ config.setSubscriptionUpdate(subscriptionUpdate);
+
+ // Subscription parameters.
+ config.setSubscriptionCreationTimeInMillis(format.parse("2016-02-01T10:00:00Z").getTime());
+ config.setSubscriptionExpirationTimeInMillis(
+ format.parse("2016-03-01T10:00:00Z").getTime());
+ config.setSubscriptionType("Gold");
+ config.setUsageLimitDataLimit(921890);
+ config.setUsageLimitStartTimeInMillis(format.parse("2016-12-01T10:00:00Z").getTime());
+ config.setUsageLimitTimeLimitInMinutes(120);
+ config.setUsageLimitUsageTimePeriodInMinutes(99910);
+
+ // HomeSP configuration.
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFriendlyName("Century House");
+ homeSp.setFqdn("mi6.co.uk");
+ homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L});
+ homeSp.setIconUrl("icon.test.com");
+ Map<String, Long> homeNetworkIds = new HashMap<>();
+ homeNetworkIds.put("TestSSID", 0x12345678L);
+ homeNetworkIds.put("NullHESSID", null);
+ homeSp.setHomeNetworkIds(homeNetworkIds);
+ homeSp.setMatchAllOis(new long[] {0x11223344});
+ homeSp.setMatchAnyOis(new long[] {0x55667788});
+ homeSp.setOtherHomePartners(new String[] {"other.fqdn.com"});
+ config.setHomeSp(homeSp);
+
+ // Credential configuration.
+ Credential credential = new Credential();
+ credential.setCreationTimeInMillis(format.parse("2016-01-01T10:00:00Z").getTime());
+ credential.setExpirationTimeInMillis(format.parse("2016-02-01T10:00:00Z").getTime());
+ credential.setRealm("shaken.stirred.com");
+ credential.setCheckAaaServerCertStatus(true);
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setUsername("james");
+ userCredential.setPassword("Ym9uZDAwNw==");
+ userCredential.setMachineManaged(true);
+ userCredential.setSoftTokenApp("TestApp");
+ userCredential.setAbleToShare(true);
+ userCredential.setEapType(21);
+ userCredential.setNonEapInnerMethod("MS-CHAP-V2");
+ credential.setUserCredential(userCredential);
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertType("x509v3");
+ certCredential.setCertSha256Fingerprint(certFingerprint);
+ credential.setCertCredential(certCredential);
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi("imsi");
+ simCredential.setEapType(24);
+ credential.setSimCredential(simCredential);
+ config.setCredential(credential);
+
+ // Policy configuration.
+ Policy policy = new Policy();
+ List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>();
+ Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+ partner1.setFqdn("test1.fqdn.com");
+ partner1.setFqdnExactMatch(true);
+ partner1.setPriority(127);
+ partner1.setCountries("us,fr");
+ Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+ partner2.setFqdn("test2.fqdn.com");
+ partner2.setFqdnExactMatch(false);
+ partner2.setPriority(200);
+ partner2.setCountries("*");
+ preferredRoamingPartnerList.add(partner1);
+ preferredRoamingPartnerList.add(partner2);
+ policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList);
+ policy.setMinHomeDownlinkBandwidth(23412);
+ policy.setMinHomeUplinkBandwidth(9823);
+ policy.setMinRoamingDownlinkBandwidth(9271);
+ policy.setMinRoamingUplinkBandwidth(2315);
+ policy.setExcludedSsidList(new String[] {"excludeSSID"});
+ Map<Integer, String> requiredProtoPortMap = new HashMap<>();
+ requiredProtoPortMap.put(12, "34,92,234");
+ policy.setRequiredProtoPortMap(requiredProtoPortMap);
+ policy.setMaximumBssLoadValue(23);
+ UpdateParameter policyUpdate = new UpdateParameter();
+ policyUpdate.setUpdateIntervalInMinutes(120);
+ policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM);
+ policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP);
+ policyUpdate.setServerUri("policy.update.com");
+ policyUpdate.setUsername("updateUser");
+ policyUpdate.setBase64EncodedPassword("updatePass");
+ policyUpdate.setTrustRootCertUrl("update.cert.com");
+ policyUpdate.setTrustRootCertSha256Fingerprint(certFingerprint);
+ policy.setPolicyUpdate(policyUpdate);
+ config.setPolicy(policy);
+ return config;
+ }
+
+ /**
+ * Helper function for serializing store data to a XML block.
+ *
+ * @param share Flag indicating share or user data
+ * @return byte[]
+ * @throws Exception
+ */
+ private byte[] serializeData(boolean share) throws Exception {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ mConfigStoreData.serializeData(out, share);
+ out.flush();
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * Helper function for deserializing store data from a XML block.
+ *
+ * @param data The XML block data bytes
+ * @param share Flag indicating share or user data
+ * @throws Exception
+ */
+ private void deserializeData(byte[] data, boolean share) throws Exception {
+ final XmlPullParser in = Xml.newPullParser();
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ mConfigStoreData.deserializeData(in, in.getDepth(), share);
+ }
+
+ /**
+ * Verify that the serialization and deserialization of user store data works as expected.
+ * The data used for serialization matches the result of the deserialization.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void serializeAndDeserializeUserStoreData() throws Exception {
+ // Setup expected data.
+ List<PasspointProvider> providerList = new ArrayList<>();
+ providerList.add(new PasspointProvider(createFullPasspointConfiguration(),
+ mKeyStore, mSimAccessor, TEST_PROVIDER_ID, TEST_CREATOR_UID,
+ TEST_CA_CERTIFICATE_ALIAS, TEST_CLIENT_CERTIFICATE_ALIAS,
+ TEST_CLIENT_PRIVATE_KEY_ALIAS));
+
+ // Serialize data for user store.
+ when(mDataSource.getProviders()).thenReturn(providerList);
+ byte[] data = serializeData(false);
+
+ // Deserialize data for user store and verify the content.
+ ArgumentCaptor<ArrayList> providersCaptor = ArgumentCaptor.forClass(ArrayList.class);
+ deserializeData(data, false);
+ verify(mDataSource).setProviders(providersCaptor.capture());
+ assertEquals(providerList, providersCaptor.getValue());
+ }
+
+ /**
+ * Verify that the serialization and deserialization of share store data works as expected.
+ * The data used for serialization matches the result of the deserialization.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void serializeAndDeserializeShareStoreData() throws Exception {
+ // Setup expected data.
+ long providerIndex = 412;
+
+ // Serialize data for share store.
+ when(mDataSource.getProviderIndex()).thenReturn(providerIndex);
+ byte[] data = serializeData(true);
+
+ // Deserialize data for share store and verify the content.
+ deserializeData(data, true);
+ verify(mDataSource).setProviderIndex(providerIndex);
+ }
+
+ /**
+ * Verify that deserialization of an empty user store data doesn't cause any exception and
+ * the corresponding data source should not be updated.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void deserializeEmptyUserStoreData() throws Exception {
+ deserializeData(new byte[0], false);
+ verify(mDataSource, never()).setProviders(any(ArrayList.class));
+ }
+
+ /**
+ * Verify that deserialization of an empty share store data doesn't cause any exception
+ * and the corresponding data source should not be updated.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void deserializeEmptyShareStoreData() throws Exception {
+ deserializeData(new byte[0], true);
+ verify(mDataSource, never()).setProviderIndex(anyLong());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointEventHandlerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointEventHandlerTest.java
new file mode 100644
index 0000000..2a6c881
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointEventHandlerTest.java
@@ -0,0 +1,157 @@
+/*
+ * 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.hotspot2;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointEventHandler}.
+ * TODO(zqiu): add more test when switch over to use wificond.
+ */
+@SmallTest
+public class PasspointEventHandlerTest {
+
+ private static final String TAG = "PasspointEventHandlerTest";
+
+ private static final long BSSID = 0x112233445566L;
+ private static final String BSSID_STR = "11:22:33:44:55:66";
+ private static final String ICON_FILENAME = "icon.test";
+
+ @Mock WifiNative mWifiNative;
+ @Mock PasspointEventHandler.Callbacks mCallbacks;
+ PasspointEventHandler mHandler;
+
+ /** Sets up test. */
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ mHandler = new PasspointEventHandler(mWifiNative, mCallbacks);
+ }
+
+ /**
+ * Test for requesting Hotspot 2.0 R1 ANQP element.
+ */
+ @Test
+ public void requestR1AnqpElement() {
+ List<Constants.ANQPElementType> elementToRequest =
+ Arrays.asList(Constants.ANQPElementType.ANQPRoamingConsortium);
+ HashSet<Integer> expAnqpIds =
+ new HashSet<>(Arrays.asList(Constants.getANQPElementID(
+ Constants.ANQPElementType.ANQPRoamingConsortium)));
+ HashSet<Integer> expHs20Subtypes = new HashSet<>();
+
+ // wpa_supplicant succeeded the request.
+ when(mWifiNative.requestAnqp(eq(BSSID_STR), eq(expAnqpIds), eq(expHs20Subtypes)))
+ .thenReturn(true);
+ assertTrue(mHandler.requestANQP(BSSID, elementToRequest));
+
+ // wpa_supplicant failed the request.
+ when(mWifiNative.requestAnqp(eq(BSSID_STR), eq(expAnqpIds), eq(expHs20Subtypes)))
+ .thenReturn(false);
+ assertFalse(mHandler.requestANQP(BSSID, elementToRequest));
+ }
+
+ /**
+ * Test for requesting Hotspot 2.0 R2 ANQP element.
+ */
+ @Test
+ public void requestR2AnqpElement() {
+ List<Constants.ANQPElementType> elementToRequest =
+ Arrays.asList(Constants.ANQPElementType.HSFriendlyName);
+ HashSet<Integer> expAnqpIds = new HashSet<>();
+ HashSet<Integer> expHs20Subtypes =
+ new HashSet<>(Arrays.asList(Constants.getHS20ElementID(
+ Constants.ANQPElementType.HSFriendlyName)));
+
+ // wpa_supplicant succeeded the request.
+ when(mWifiNative.requestAnqp(eq(BSSID_STR), eq(expAnqpIds), eq(expHs20Subtypes)))
+ .thenReturn(true);
+ assertTrue(mHandler.requestANQP(BSSID, elementToRequest));
+
+ // wpa_supplicant failed the request.
+ when(mWifiNative.requestAnqp(eq(BSSID_STR), eq(expAnqpIds), eq(expHs20Subtypes)))
+ .thenReturn(false);
+ assertFalse(mHandler.requestANQP(BSSID, elementToRequest));
+ }
+
+ /**
+ * Test for requesting both Hotspot 2.0 R1 and R2 ANQP elements.
+ */
+ @Test
+ public void requestMixAnqpElements() {
+ List<Constants.ANQPElementType> elementToRequest =
+ Arrays.asList(Constants.ANQPElementType.ANQPRoamingConsortium,
+ Constants.ANQPElementType.HSFriendlyName);
+ HashSet<Integer> expAnqpIds =
+ new HashSet<>(Arrays.asList(Constants.getANQPElementID(
+ Constants.ANQPElementType.ANQPRoamingConsortium)));
+ HashSet<Integer> expHs20Subtypes =
+ new HashSet<>(Arrays.asList(Constants.getHS20ElementID(
+ Constants.ANQPElementType.HSFriendlyName)));
+
+ // wpa_supplicant succeeded the request.
+ when(mWifiNative.requestAnqp(eq(BSSID_STR), eq(expAnqpIds), eq(expHs20Subtypes)))
+ .thenReturn(true);
+ assertTrue(mHandler.requestANQP(BSSID, elementToRequest));
+
+ // wpa_supplicant failed the request.
+ when(mWifiNative.requestAnqp(eq(BSSID_STR), eq(expAnqpIds), eq(expHs20Subtypes)))
+ .thenReturn(false);
+ assertFalse(mHandler.requestANQP(BSSID, elementToRequest));
+ }
+
+ /**
+ * Test for requesting both Hotspot 2.0 R2 icon file.
+ */
+ @Test
+ public void requestIconFile() {
+ // wpa_supplicant succeeded the request.
+ when(mWifiNative.requestIcon(eq(BSSID_STR), eq(ICON_FILENAME))).thenReturn(true);
+ assertTrue(mHandler.requestIcon(BSSID, ICON_FILENAME));
+
+ // wpa_supplicant failed the request.
+ when(mWifiNative.requestIcon(eq(BSSID_STR), eq(ICON_FILENAME))).thenReturn(false);
+ assertFalse(mHandler.requestIcon(BSSID, ICON_FILENAME));
+ }
+
+ /**
+ * Test for ANQP request completed with error.
+ */
+ @Test
+ public void anqpRequestCompletedWithError() {
+ mHandler.notifyANQPDone(new AnqpEvent(BSSID, null));
+ verify(mCallbacks).onANQPResponse(BSSID, null);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
new file mode 100644
index 0000000..70ae354
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java
@@ -0,0 +1,1086 @@
+/*
+ * 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.hotspot2;
+
+import static android.net.wifi.WifiManager.ACTION_PASSPOINT_DEAUTH_IMMINENT;
+import static android.net.wifi.WifiManager.ACTION_PASSPOINT_ICON;
+import static android.net.wifi.WifiManager.ACTION_PASSPOINT_OSU_PROVIDERS_LIST;
+import static android.net.wifi.WifiManager.ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION;
+import static android.net.wifi.WifiManager.EXTRA_ANQP_ELEMENT_DATA;
+import static android.net.wifi.WifiManager.EXTRA_BSSID_LONG;
+import static android.net.wifi.WifiManager.EXTRA_DELAY;
+import static android.net.wifi.WifiManager.EXTRA_ESS;
+import static android.net.wifi.WifiManager.EXTRA_FILENAME;
+import static android.net.wifi.WifiManager.EXTRA_ICON;
+import static android.net.wifi.WifiManager.EXTRA_SUBSCRIPTION_REMEDIATION_METHOD;
+import static android.net.wifi.WifiManager.EXTRA_URL;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyMap;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.net.wifi.EAPConstants;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.os.UserHandle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+import android.util.Pair;
+
+import com.android.server.wifi.Clock;
+import com.android.server.wifi.FakeKeys;
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.SIMAccessor;
+import com.android.server.wifi.WifiConfigManager;
+import com.android.server.wifi.WifiConfigStore;
+import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
+import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
+import com.android.server.wifi.hotspot2.anqp.RawByteElement;
+import com.android.server.wifi.util.ScanResultUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointManager}.
+ */
+@SmallTest
+public class PasspointManagerTest {
+ private static final long BSSID = 0x112233445566L;
+ private static final String ICON_FILENAME = "test";
+ private static final String TEST_FQDN = "test1.test.com";
+ private static final String TEST_FRIENDLY_NAME = "friendly name";
+ private static final String TEST_REALM = "realm.test.com";
+ private static final String TEST_IMSI = "1234*";
+ private static final IMSIParameter TEST_IMSI_PARAM = IMSIParameter.build(TEST_IMSI);
+
+ private static final String TEST_SSID = "TestSSID";
+ private static final long TEST_BSSID = 0x112233445566L;
+ private static final String TEST_BSSID_STRING = "11:22:33:44:55:66";
+ private static final long TEST_HESSID = 0x5678L;
+ private static final int TEST_ANQP_DOMAIN_ID = 0;
+ private static final ANQPNetworkKey TEST_ANQP_KEY = ANQPNetworkKey.buildKey(
+ TEST_SSID, TEST_BSSID, TEST_HESSID, TEST_ANQP_DOMAIN_ID);
+ private static final int TEST_CREATOR_UID = 1234;
+
+ @Mock Context mContext;
+ @Mock WifiNative mWifiNative;
+ @Mock WifiKeyStore mWifiKeyStore;
+ @Mock Clock mClock;
+ @Mock SIMAccessor mSimAccessor;
+ @Mock PasspointObjectFactory mObjectFactory;
+ @Mock PasspointEventHandler.Callbacks mCallbacks;
+ @Mock AnqpCache mAnqpCache;
+ @Mock ANQPRequestManager mAnqpRequestManager;
+ @Mock CertificateVerifier mCertVerifier;
+ @Mock WifiConfigManager mWifiConfigManager;
+ @Mock WifiConfigStore mWifiConfigStore;
+ @Mock PasspointConfigStoreData.DataSource mDataSource;
+ PasspointManager mManager;
+
+ /** Sets up test. */
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ when(mObjectFactory.makeAnqpCache(mClock)).thenReturn(mAnqpCache);
+ when(mObjectFactory.makeANQPRequestManager(any(), eq(mClock)))
+ .thenReturn(mAnqpRequestManager);
+ when(mObjectFactory.makeCertificateVerifier()).thenReturn(mCertVerifier);
+ mManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock,
+ mSimAccessor, mObjectFactory, mWifiConfigManager, mWifiConfigStore);
+ ArgumentCaptor<PasspointEventHandler.Callbacks> callbacks =
+ ArgumentCaptor.forClass(PasspointEventHandler.Callbacks.class);
+ verify(mObjectFactory).makePasspointEventHandler(any(WifiNative.class),
+ callbacks.capture());
+ ArgumentCaptor<PasspointConfigStoreData.DataSource> dataSource =
+ ArgumentCaptor.forClass(PasspointConfigStoreData.DataSource.class);
+ verify(mObjectFactory).makePasspointConfigStoreData(
+ any(WifiKeyStore.class), any(SIMAccessor.class), dataSource.capture());
+ mCallbacks = callbacks.getValue();
+ mDataSource = dataSource.getValue();
+ }
+
+ /**
+ * Verify {@link WifiManager#ACTION_PASSPOINT_ICON} broadcast intent.
+ * @param bssid BSSID of the AP
+ * @param fileName Name of the icon file
+ * @param data icon data byte array
+ */
+ private void verifyIconIntent(long bssid, String fileName, byte[] data) {
+ ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL),
+ eq(android.Manifest.permission.ACCESS_WIFI_STATE));
+ assertEquals(ACTION_PASSPOINT_ICON, intent.getValue().getAction());
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_BSSID_LONG));
+ assertEquals(bssid, intent.getValue().getExtras().getLong(EXTRA_BSSID_LONG));
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_FILENAME));
+ assertEquals(fileName, intent.getValue().getExtras().getString(EXTRA_FILENAME));
+ if (data != null) {
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_ICON));
+ Icon icon = (Icon) intent.getValue().getExtras().getParcelable(EXTRA_ICON);
+ assertTrue(Arrays.equals(data, icon.getDataBytes()));
+ } else {
+ assertFalse(intent.getValue().getExtras().containsKey(EXTRA_ICON));
+ }
+ }
+
+ /**
+ * Verify that the given Passpoint configuration matches the one that's added to
+ * the PasspointManager.
+ *
+ * @param expectedConfig The expected installed Passpoint configuration
+ */
+ private void verifyInstalledConfig(PasspointConfiguration expectedConfig) {
+ List<PasspointConfiguration> installedConfigs = mManager.getProviderConfigs();
+ assertEquals(1, installedConfigs.size());
+ assertEquals(expectedConfig, installedConfigs.get(0));
+ }
+
+ /**
+ * Create a mock PasspointProvider with default expectations.
+ *
+ * @param config The configuration associated with the provider
+ * @return {@link com.android.server.wifi.hotspot2.PasspointProvider}
+ */
+ private PasspointProvider createMockProvider(PasspointConfiguration config) {
+ PasspointProvider provider = mock(PasspointProvider.class);
+ when(provider.installCertsAndKeys()).thenReturn(true);
+ when(provider.getConfig()).thenReturn(config);
+ return provider;
+ }
+
+ /**
+ * Helper function for creating a test configuration with user credential.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ private PasspointConfiguration createTestConfigWithUserCredential() {
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(TEST_FQDN);
+ homeSp.setFriendlyName(TEST_FRIENDLY_NAME);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setRealm(TEST_REALM);
+ credential.setCaCertificate(FakeKeys.CA_CERT0);
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setUsername("username");
+ userCredential.setPassword("password");
+ userCredential.setEapType(EAPConstants.EAP_TTLS);
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAP);
+ credential.setUserCredential(userCredential);
+ config.setCredential(credential);
+ return config;
+ }
+
+ /**
+ * Helper function for creating a test configuration with SIM credential.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ private PasspointConfiguration createTestConfigWithSimCredential() {
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(TEST_FQDN);
+ homeSp.setFriendlyName(TEST_FRIENDLY_NAME);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setRealm(TEST_REALM);
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi(TEST_IMSI);
+ simCredential.setEapType(EAPConstants.EAP_SIM);
+ credential.setSimCredential(simCredential);
+ config.setCredential(credential);
+ return config;
+ }
+
+ /**
+ * Helper function for adding a test provider to the manager. Return the mock
+ * provider that's added to the manager.
+ *
+ * @return {@link PasspointProvider}
+ */
+ private PasspointProvider addTestProvider() {
+ PasspointConfiguration config = createTestConfigWithUserCredential();
+ PasspointProvider provider = createMockProvider(config);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(provider);
+ assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+
+ return provider;
+ }
+
+ /**
+ * Helper function for creating a ScanResult for testing.
+ *
+ * @return {@link ScanResult}
+ */
+ private ScanResult createTestScanResult() {
+ ScanResult scanResult = new ScanResult();
+ scanResult.SSID = TEST_SSID;
+ scanResult.BSSID = TEST_BSSID_STRING;
+ scanResult.hessid = TEST_HESSID;
+ return scanResult;
+ }
+
+ /**
+ * Verify that the ANQP elements will be added to the ANQP cache on receiving a successful
+ * response.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void anqpResponseSuccess() throws Exception {
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ new DomainNameElement(Arrays.asList(new String[] {"test.com"})));
+
+ when(mAnqpRequestManager.onRequestCompleted(TEST_BSSID, true)).thenReturn(TEST_ANQP_KEY);
+ mCallbacks.onANQPResponse(TEST_BSSID, anqpElementMap);
+ verify(mAnqpCache).addEntry(TEST_ANQP_KEY, anqpElementMap);
+ verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class),
+ any(String.class));
+ }
+
+ /**
+ * Verify that the ANQP elements will be added to the AQNP cache and an
+ * {@link WifiManager#ACTION_PASSPOINT_OSU_PROVIDER_LIST} intent will be broadcasted when
+ * receiving an ANQP response containing OSU Providers element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void anqpResponseWithOSUProviders() throws Exception {
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ byte[] testData = new byte[] {0x12, 0x34, 0x56, 0x78};
+ anqpElementMap.put(ANQPElementType.HSOSUProviders,
+ new RawByteElement(ANQPElementType.HSOSUProviders, testData));
+
+ when(mAnqpRequestManager.onRequestCompleted(TEST_BSSID, true)).thenReturn(TEST_ANQP_KEY);
+ mCallbacks.onANQPResponse(TEST_BSSID, anqpElementMap);
+ verify(mAnqpCache).addEntry(TEST_ANQP_KEY, anqpElementMap);
+
+ // Verify the broadcast intent for OSU providers.
+ ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL),
+ eq(android.Manifest.permission.ACCESS_WIFI_STATE));
+ assertEquals(ACTION_PASSPOINT_OSU_PROVIDERS_LIST, intent.getValue().getAction());
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_BSSID_LONG));
+ assertEquals(TEST_BSSID, intent.getValue().getExtras().getLong(EXTRA_BSSID_LONG));
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_ANQP_ELEMENT_DATA));
+ assertTrue(Arrays.equals(testData,
+ intent.getValue().getExtras().getByteArray(EXTRA_ANQP_ELEMENT_DATA)));
+ }
+
+ /**
+ * Verify that no ANQP elements will be added to the ANQP cache on receiving a successful
+ * response for a request that's not sent by us.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void anqpResponseSuccessWithUnknownRequest() throws Exception {
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ new DomainNameElement(Arrays.asList(new String[] {"test.com"})));
+
+ when(mAnqpRequestManager.onRequestCompleted(TEST_BSSID, true)).thenReturn(null);
+ mCallbacks.onANQPResponse(TEST_BSSID, anqpElementMap);
+ verify(mAnqpCache, never()).addEntry(any(ANQPNetworkKey.class), anyMap());
+ }
+
+ /**
+ * Verify that no ANQP elements will be added to the ANQP cache on receiving a failure response.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void anqpResponseFailure() throws Exception {
+ when(mAnqpRequestManager.onRequestCompleted(TEST_BSSID, false)).thenReturn(TEST_ANQP_KEY);
+ mCallbacks.onANQPResponse(TEST_BSSID, null);
+ verify(mAnqpCache, never()).addEntry(any(ANQPNetworkKey.class), anyMap());
+
+ }
+
+ /**
+ * Validate the broadcast intent when icon file retrieval succeeded.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void iconResponseSuccess() throws Exception {
+ byte[] iconData = new byte[] {0x00, 0x11};
+ mCallbacks.onIconResponse(BSSID, ICON_FILENAME, iconData);
+ verifyIconIntent(BSSID, ICON_FILENAME, iconData);
+ }
+
+ /**
+ * Validate the broadcast intent when icon file retrieval failed.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void iconResponseFailure() throws Exception {
+ mCallbacks.onIconResponse(BSSID, ICON_FILENAME, null);
+ verifyIconIntent(BSSID, ICON_FILENAME, null);
+ }
+
+ /**
+ * Validate the broadcast intent {@link WifiManager#ACTION_PASSPOINT_DEAUTH_IMMINENT} when
+ * Deauth Imminent WNM frame is received.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void onDeauthImminentReceived() throws Exception {
+ String reasonUrl = "test.com";
+ int delay = 123;
+ boolean ess = true;
+
+ mCallbacks.onWnmFrameReceived(new WnmData(BSSID, reasonUrl, ess, delay));
+ // Verify the broadcast intent.
+ ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL),
+ eq(android.Manifest.permission.ACCESS_WIFI_STATE));
+ assertEquals(ACTION_PASSPOINT_DEAUTH_IMMINENT, intent.getValue().getAction());
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_BSSID_LONG));
+ assertEquals(BSSID, intent.getValue().getExtras().getLong(EXTRA_BSSID_LONG));
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_ESS));
+ assertEquals(ess, intent.getValue().getExtras().getBoolean(EXTRA_ESS));
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_DELAY));
+ assertEquals(delay, intent.getValue().getExtras().getInt(EXTRA_DELAY));
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_URL));
+ assertEquals(reasonUrl, intent.getValue().getExtras().getString(EXTRA_URL));
+ }
+
+ /**
+ * Validate the broadcast intent {@link WifiManager#ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION}
+ * when Subscription Remediation WNM frame is received.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void onSubscriptionRemediationReceived() throws Exception {
+ int serverMethod = 1;
+ String serverUrl = "testUrl";
+
+ mCallbacks.onWnmFrameReceived(new WnmData(BSSID, serverUrl, serverMethod));
+ // Verify the broadcast intent.
+ ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcastAsUser(intent.capture(), eq(UserHandle.ALL),
+ eq(android.Manifest.permission.ACCESS_WIFI_STATE));
+ assertEquals(ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION, intent.getValue().getAction());
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_BSSID_LONG));
+ assertEquals(BSSID, intent.getValue().getExtras().getLong(EXTRA_BSSID_LONG));
+ assertTrue(intent.getValue().getExtras().containsKey(
+ EXTRA_SUBSCRIPTION_REMEDIATION_METHOD));
+ assertEquals(serverMethod, intent.getValue().getExtras().getInt(
+ EXTRA_SUBSCRIPTION_REMEDIATION_METHOD));
+ assertTrue(intent.getValue().getExtras().containsKey(EXTRA_URL));
+ assertEquals(serverUrl, intent.getValue().getExtras().getString(EXTRA_URL));
+ }
+
+ /**
+ * Verify that adding a provider with a null configuration will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addProviderWithNullConfig() throws Exception {
+ assertFalse(mManager.addOrUpdateProvider(null, TEST_CREATOR_UID));
+ }
+
+ /**
+ * Verify that adding a provider with a empty configuration will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addProviderWithEmptyConfig() throws Exception {
+ assertFalse(mManager.addOrUpdateProvider(new PasspointConfiguration(), TEST_CREATOR_UID));
+ }
+
+ /**
+ * Verify taht adding a provider with an invalid credential will fail (using EAP-TLS
+ * for user credential).
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addProviderWithInvalidCredential() throws Exception {
+ PasspointConfiguration config = createTestConfigWithUserCredential();
+ // EAP-TLS not allowed for user credential.
+ config.getCredential().getUserCredential().setEapType(EAPConstants.EAP_TLS);
+ assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ }
+
+ /**
+ * Verify that adding a provider with a valid configuration and user credential will succeed.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addRemoveProviderWithValidUserCredential() throws Exception {
+ PasspointConfiguration config = createTestConfigWithUserCredential();
+ PasspointProvider provider = createMockProvider(config);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(provider);
+ assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ verifyInstalledConfig(config);
+ verify(mWifiConfigManager).saveToStore(true);
+ reset(mWifiConfigManager);
+
+ // Verify content in the data source.
+ List<PasspointProvider> providers = mDataSource.getProviders();
+ assertEquals(1, providers.size());
+ assertEquals(config, providers.get(0).getConfig());
+ // Provider index start with 0, should be 1 after adding a provider.
+ assertEquals(1, mDataSource.getProviderIndex());
+
+ // Remove the provider.
+ assertTrue(mManager.removeProvider(TEST_FQDN));
+ verify(provider).uninstallCertsAndKeys();
+ verify(mWifiConfigManager).saveToStore(true);
+ assertTrue(mManager.getProviderConfigs().isEmpty());
+
+ // Verify content in the data source.
+ assertTrue(mDataSource.getProviders().isEmpty());
+ // Removing a provider should not change the provider index.
+ assertEquals(1, mDataSource.getProviderIndex());
+ }
+
+ /**
+ * Verify that adding a provider with a valid configuration and SIM credential will succeed.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addRemoveProviderWithValidSimCredential() throws Exception {
+ PasspointConfiguration config = createTestConfigWithSimCredential();
+ PasspointProvider provider = createMockProvider(config);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(provider);
+ assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ verifyInstalledConfig(config);
+ verify(mWifiConfigManager).saveToStore(true);
+ reset(mWifiConfigManager);
+
+ // Verify content in the data source.
+ List<PasspointProvider> providers = mDataSource.getProviders();
+ assertEquals(1, providers.size());
+ assertEquals(config, providers.get(0).getConfig());
+ // Provider index start with 0, should be 1 after adding a provider.
+ assertEquals(1, mDataSource.getProviderIndex());
+
+ // Remove the provider.
+ assertTrue(mManager.removeProvider(TEST_FQDN));
+ verify(provider).uninstallCertsAndKeys();
+ verify(mWifiConfigManager).saveToStore(true);
+ assertTrue(mManager.getProviderConfigs().isEmpty());
+
+ // Verify content in the data source.
+ assertTrue(mDataSource.getProviders().isEmpty());
+ // Removing a provider should not change the provider index.
+ assertEquals(1, mDataSource.getProviderIndex());
+ }
+
+ /**
+ * Verify that adding a provider with the same base domain as the existing provider will
+ * succeed, and verify that the existing provider is replaced by the new provider with
+ * the new configuration.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addProviderWithExistingConfig() throws Exception {
+ // Add a provider with the original configuration.
+ PasspointConfiguration origConfig = createTestConfigWithSimCredential();
+ PasspointProvider origProvider = createMockProvider(origConfig);
+ when(mObjectFactory.makePasspointProvider(eq(origConfig), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(origProvider);
+ assertTrue(mManager.addOrUpdateProvider(origConfig, TEST_CREATOR_UID));
+ verifyInstalledConfig(origConfig);
+ verify(mWifiConfigManager).saveToStore(true);
+ reset(mWifiConfigManager);
+
+ // Verify data source content.
+ List<PasspointProvider> origProviders = mDataSource.getProviders();
+ assertEquals(1, origProviders.size());
+ assertEquals(origConfig, origProviders.get(0).getConfig());
+ assertEquals(1, mDataSource.getProviderIndex());
+
+ // Add another provider with the same base domain as the existing provider.
+ // This should replace the existing provider with the new configuration.
+ PasspointConfiguration newConfig = createTestConfigWithUserCredential();
+ PasspointProvider newProvider = createMockProvider(newConfig);
+ when(mObjectFactory.makePasspointProvider(eq(newConfig), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(newProvider);
+ assertTrue(mManager.addOrUpdateProvider(newConfig, TEST_CREATOR_UID));
+ verifyInstalledConfig(newConfig);
+ verify(mWifiConfigManager).saveToStore(true);
+
+ // Verify data source content.
+ List<PasspointProvider> newProviders = mDataSource.getProviders();
+ assertEquals(1, newProviders.size());
+ assertEquals(newConfig, newProviders.get(0).getConfig());
+ assertEquals(2, mDataSource.getProviderIndex());
+ }
+
+ /**
+ * Verify that adding a provider will fail when failing to install certificates and
+ * key to the keystore.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addProviderOnKeyInstallationFailiure() throws Exception {
+ PasspointConfiguration config = createTestConfigWithUserCredential();
+ PasspointProvider provider = mock(PasspointProvider.class);
+ when(provider.installCertsAndKeys()).thenReturn(false);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(provider);
+ assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ }
+
+ /**
+ * Verify that adding a provider with an invalid CA certificate will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addProviderWithInvalidCaCert() throws Exception {
+ PasspointConfiguration config = createTestConfigWithUserCredential();
+ doThrow(new GeneralSecurityException())
+ .when(mCertVerifier).verifyCaCert(any(X509Certificate.class));
+ assertFalse(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ }
+
+ /**
+ * Verify that adding a provider with R2 configuration will not perform CA certificate
+ * verification.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addProviderWithR2Config() throws Exception {
+ PasspointConfiguration config = createTestConfigWithUserCredential();
+ config.setUpdateIdentifier(1);
+ PasspointProvider provider = createMockProvider(config);
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), anyLong(), eq(TEST_CREATOR_UID))).thenReturn(provider);
+ assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ verify(mCertVerifier, never()).verifyCaCert(any(X509Certificate.class));
+ verifyInstalledConfig(config);
+ }
+
+ /**
+ * Verify that removing a non-existing provider will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void removeNonExistingProvider() throws Exception {
+ assertFalse(mManager.removeProvider(TEST_FQDN));
+ }
+
+ /**
+ * Verify that a {code null} will be returned when no providers are installed.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchProviderWithNoProvidersInstalled() throws Exception {
+ assertNull(mManager.matchProvider(createTestScanResult()));
+ }
+
+ /**
+ * Verify that a {code null} be returned when ANQP entry doesn't exist in the cache.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchProviderWithAnqpCacheMissed() throws Exception {
+ addTestProvider();
+
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(null);
+ assertNull(mManager.matchProvider(createTestScanResult()));
+ // Verify that a request for ANQP elements is initiated.
+ verify(mAnqpRequestManager).requestANQPElements(eq(TEST_BSSID), any(ANQPNetworkKey.class),
+ anyBoolean(), anyBoolean());
+ }
+
+ /**
+ * Verify that the expected provider will be returned when a HomeProvider is matched.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchProviderAsHomeProvider() throws Exception {
+ PasspointProvider provider = addTestProvider();
+ ANQPData entry = new ANQPData(mClock, null);
+
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
+ when(provider.match(anyMap())).thenReturn(PasspointMatch.HomeProvider);
+ Pair<PasspointProvider, PasspointMatch> result =
+ mManager.matchProvider(createTestScanResult());
+ assertEquals(PasspointMatch.HomeProvider, result.second);
+ assertEquals(TEST_FQDN, result.first.getConfig().getHomeSp().getFqdn());
+ }
+
+ /**
+ * Verify that the expected provider will be returned when a RoamingProvider is matched.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchProviderAsRoamingProvider() throws Exception {
+ PasspointProvider provider = addTestProvider();
+ ANQPData entry = new ANQPData(mClock, null);
+
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
+ when(provider.match(anyMap())).thenReturn(PasspointMatch.RoamingProvider);
+ Pair<PasspointProvider, PasspointMatch> result =
+ mManager.matchProvider(createTestScanResult());
+ assertEquals(PasspointMatch.RoamingProvider, result.second);
+ assertEquals(TEST_FQDN, result.first.getConfig().getHomeSp().getFqdn());
+ }
+
+ /**
+ * Verify that a {code null} will be returned when there is no matching provider.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchProviderWithNoMatch() throws Exception {
+ PasspointProvider provider = addTestProvider();
+ ANQPData entry = new ANQPData(mClock, null);
+
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
+ when(provider.match(anyMap())).thenReturn(PasspointMatch.None);
+ assertNull(mManager.matchProvider(createTestScanResult()));
+ }
+
+ /**
+ * Verify the expectations for sweepCache.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void sweepCache() throws Exception {
+ mManager.sweepCache();
+ verify(mAnqpCache).sweep();
+ }
+
+ /**
+ * Verify that an empty map will be returned if ANQP elements are not cached for the given AP.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getANQPElementsWithNoMatchFound() throws Exception {
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(null);
+ assertTrue(mManager.getANQPElements(createTestScanResult()).isEmpty());
+ }
+
+ /**
+ * Verify that an expected ANQP elements will be returned if ANQP elements are cached for the
+ * given AP.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getANQPElementsWithMatchFound() throws Exception {
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ new DomainNameElement(Arrays.asList(new String[] {"test.com"})));
+ ANQPData entry = new ANQPData(mClock, anqpElementMap);
+
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
+ assertEquals(anqpElementMap, mManager.getANQPElements(createTestScanResult()));
+ }
+
+ /**
+ * Verify that an expected {@link WifiConfiguration} will be returned when a {@link ScanResult}
+ * is matched to a home provider.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingWifiConfigForHomeProviderAP() throws Exception {
+ PasspointProvider provider = addTestProvider();
+ ANQPData entry = new ANQPData(mClock, null);
+
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
+ when(provider.match(anyMap())).thenReturn(PasspointMatch.HomeProvider);
+ when(provider.getWifiConfig()).thenReturn(new WifiConfiguration());
+ WifiConfiguration config = mManager.getMatchingWifiConfig(createTestScanResult());
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID), config.SSID);
+ assertTrue(config.isHomeProviderNetwork);
+ }
+
+ /**
+ * Verify that an expected {@link WifiConfiguration} will be returned when a {@link ScanResult}
+ * is matched to a roaming provider.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingWifiConfigForRoamingProviderAP() throws Exception {
+ PasspointProvider provider = addTestProvider();
+ ANQPData entry = new ANQPData(mClock, null);
+
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
+ when(provider.match(anyMap())).thenReturn(PasspointMatch.RoamingProvider);
+ when(provider.getWifiConfig()).thenReturn(new WifiConfiguration());
+ WifiConfiguration config = mManager.getMatchingWifiConfig(createTestScanResult());
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID), config.SSID);
+ assertFalse(config.isHomeProviderNetwork);
+ }
+
+ /**
+ * Verify that a {code null} will be returned when a {@link ScanResult} doesn't match any
+ * provider.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingWifiConfigWithNoMatchingProvider() throws Exception {
+ PasspointProvider provider = addTestProvider();
+ ANQPData entry = new ANQPData(mClock, null);
+
+ when(mAnqpCache.getEntry(TEST_ANQP_KEY)).thenReturn(entry);
+ when(provider.match(anyMap())).thenReturn(PasspointMatch.None);
+ assertNull(mManager.getMatchingWifiConfig(createTestScanResult()));
+ verify(provider, never()).getWifiConfig();
+ }
+
+ /**
+ * Verify that a {@code null} will returned when trying to get a matching
+ * {@link WifiConfiguration} a {@code null} {@link ScanResult}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getMatchingWifiConfigWithNullScanResult() throws Exception {
+ assertNull(mManager.getMatchingWifiConfig(null));
+ }
+
+ /**
+ * Verify that the provider list maintained by the PasspointManager after the list is updated
+ * in the data source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyProvidersAfterDataSourceUpdate() throws Exception {
+ // Update the provider list in the data source.
+ PasspointConfiguration config = createTestConfigWithUserCredential();
+ PasspointProvider provider = createMockProvider(config);
+ List<PasspointProvider> providers = new ArrayList<>();
+ providers.add(provider);
+ mDataSource.setProviders(providers);
+
+ // Verify the providers maintained by PasspointManager.
+ assertEquals(1, mManager.getProviderConfigs().size());
+ assertEquals(config, mManager.getProviderConfigs().get(0));
+ }
+
+ /**
+ * Verify that the provider index used by PasspointManager is updated after it is updated in
+ * the data source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyProviderIndexAfterDataSourceUpdate() throws Exception {
+ long providerIndex = 9;
+ mDataSource.setProviderIndex(providerIndex);
+ assertEquals(providerIndex, mDataSource.getProviderIndex());
+
+ // Add a provider.
+ PasspointConfiguration config = createTestConfigWithUserCredential();
+ PasspointProvider provider = createMockProvider(config);
+ // Verify the provider ID used to create the new provider.
+ when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore),
+ eq(mSimAccessor), eq(providerIndex), eq(TEST_CREATOR_UID))).thenReturn(provider);
+ assertTrue(mManager.addOrUpdateProvider(config, TEST_CREATOR_UID));
+ verifyInstalledConfig(config);
+ verify(mWifiConfigManager).saveToStore(true);
+ reset(mWifiConfigManager);
+ }
+
+ /**
+ * Verify that a PasspointProvider with expected PasspointConfiguration will be installed when
+ * adding a legacy Passpoint configuration containing a valid user credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addLegacyPasspointConfigWithUserCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String username = "username";
+ String password = "password";
+ byte[] base64EncodedPw =
+ Base64.encode(password.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
+ String encodedPasswordStr = new String(base64EncodedPw, StandardCharsets.UTF_8);
+ String caCertificateAlias = "CaCert";
+
+ // Setup WifiConfiguration for legacy Passpoint configuraiton.
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = friendlyName;
+ wifiConfig.roamingConsortiumIds = rcOIs;
+ wifiConfig.enterpriseConfig.setIdentity(username);
+ wifiConfig.enterpriseConfig.setPassword(password);
+ wifiConfig.enterpriseConfig.setRealm(realm);
+ wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
+ wifiConfig.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.PAP);
+ wifiConfig.enterpriseConfig.setCaCertificateAlias(caCertificateAlias);
+
+ // Setup expected {@link PasspointConfiguration}
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ passpointConfig.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setUsername(username);
+ userCredential.setPassword(encodedPasswordStr);
+ userCredential.setEapType(EAPConstants.EAP_TTLS);
+ userCredential.setNonEapInnerMethod("PAP");
+ credential.setUserCredential(userCredential);
+ credential.setRealm(realm);
+ passpointConfig.setCredential(credential);
+
+ assertTrue(PasspointManager.addLegacyPasspointConfig(wifiConfig));
+ verifyInstalledConfig(passpointConfig);
+ }
+
+ /**
+ * Verify that adding a legacy Passpoint configuration containing user credential will
+ * fail when client certificate is not provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addLegacyPasspointConfigWithUserCredentialWithoutCaCert() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String username = "username";
+ String password = "password";
+ byte[] base64EncodedPw =
+ Base64.encode(password.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
+ String encodedPasswordStr = new String(base64EncodedPw, StandardCharsets.UTF_8);
+
+ // Setup WifiConfiguration for legacy Passpoint configuraiton.
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = friendlyName;
+ wifiConfig.roamingConsortiumIds = rcOIs;
+ wifiConfig.enterpriseConfig.setIdentity(username);
+ wifiConfig.enterpriseConfig.setPassword(password);
+ wifiConfig.enterpriseConfig.setRealm(realm);
+ wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
+ wifiConfig.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.PAP);
+
+ assertFalse(PasspointManager.addLegacyPasspointConfig(wifiConfig));
+ }
+
+ /**
+ * Verify that a PasspointProvider with expected PasspointConfiguration will be installed when
+ * adding a legacy Passpoint configuration containing a valid SIM credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addLegacyPasspointConfigWithSimCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String imsi = "1234";
+
+ // Setup WifiConfiguration for legacy Passpoint configuraiton.
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = friendlyName;
+ wifiConfig.roamingConsortiumIds = rcOIs;
+ wifiConfig.enterpriseConfig.setRealm(realm);
+ wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM);
+ wifiConfig.enterpriseConfig.setPlmn(imsi);
+
+ // Setup expected {@link PasspointConfiguration}
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ passpointConfig.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setEapType(EAPConstants.EAP_SIM);
+ simCredential.setImsi(imsi);
+ credential.setSimCredential(simCredential);
+ credential.setRealm(realm);
+ passpointConfig.setCredential(credential);
+
+ assertTrue(PasspointManager.addLegacyPasspointConfig(wifiConfig));
+ verifyInstalledConfig(passpointConfig);
+ }
+
+ /**
+ * Verify that a PasspointProvider with expected PasspointConfiguration will be installed when
+ * adding a legacy Passpoint configuration containing a valid certificate credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addLegacyPasspointConfigWithCertCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String caCertificateAlias = "CaCert";
+ String clientCertificateAlias = "ClientCert";
+
+ // Setup WifiConfiguration for legacy Passpoint configuraiton.
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = friendlyName;
+ wifiConfig.roamingConsortiumIds = rcOIs;
+ wifiConfig.enterpriseConfig.setRealm(realm);
+ wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ wifiConfig.enterpriseConfig.setCaCertificateAlias(caCertificateAlias);
+ wifiConfig.enterpriseConfig.setClientCertificateAlias(clientCertificateAlias);
+
+ // Setup expected {@link PasspointConfiguration}
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ passpointConfig.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3);
+ credential.setCertCredential(certCredential);
+ credential.setRealm(realm);
+ passpointConfig.setCredential(credential);
+
+ assertTrue(PasspointManager.addLegacyPasspointConfig(wifiConfig));
+ verifyInstalledConfig(passpointConfig);
+ }
+
+ /**
+ * Verify that adding a legacy Passpoint configuration containing certificate credential will
+ * fail when CA certificate is not provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addLegacyPasspointConfigWithCertCredentialWithoutCaCert() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String clientCertificateAlias = "ClientCert";
+
+ // Setup WifiConfiguration for legacy Passpoint configuraiton.
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = friendlyName;
+ wifiConfig.roamingConsortiumIds = rcOIs;
+ wifiConfig.enterpriseConfig.setRealm(realm);
+ wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ wifiConfig.enterpriseConfig.setClientCertificateAlias(clientCertificateAlias);
+
+ assertFalse(PasspointManager.addLegacyPasspointConfig(wifiConfig));
+ }
+
+ /**
+ * Verify that adding a legacy Passpoint configuration containing certificate credential will
+ * fail when client certificate is not provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void addLegacyPasspointConfigWithCertCredentialWithoutClientCert() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String caCertificateAlias = "CaCert";
+
+ // Setup WifiConfiguration for legacy Passpoint configuraiton.
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = friendlyName;
+ wifiConfig.roamingConsortiumIds = rcOIs;
+ wifiConfig.enterpriseConfig.setRealm(realm);
+ wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+ wifiConfig.enterpriseConfig.setCaCertificateAlias(caCertificateAlias);
+
+ assertFalse(PasspointManager.addLegacyPasspointConfig(wifiConfig));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
new file mode 100644
index 0000000..2224486
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkEvaluatorTest.java
@@ -0,0 +1,336 @@
+/*
+ * 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.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.LocalLog;
+import android.util.Pair;
+
+import com.android.server.wifi.NetworkUpdateResult;
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.WifiConfigManager;
+import com.android.server.wifi.util.ScanResultUtil;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointNetworkEvaluator}.
+ */
+@SmallTest
+public class PasspointNetworkEvaluatorTest {
+ private static final int TEST_NETWORK_ID = 1;
+ private static final String TEST_SSID1 = "ssid1";
+ private static final String TEST_SSID2 = "ssid2";
+ private static final String TEST_FQDN1 = "test1.com";
+ private static final String TEST_FQDN2 = "test2.com";
+ private static final WifiConfiguration TEST_CONFIG1 = generateWifiConfig(TEST_FQDN1);
+ private static final WifiConfiguration TEST_CONFIG2 = generateWifiConfig(TEST_FQDN2);
+ private static final PasspointProvider TEST_PROVIDER1 = generateProvider(TEST_CONFIG1);
+ private static final PasspointProvider TEST_PROVIDER2 = generateProvider(TEST_CONFIG2);
+
+ @Mock PasspointManager mPasspointManager;
+ @Mock WifiConfigManager mWifiConfigManager;
+ LocalLog mLocalLog;
+ PasspointNetworkEvaluator mEvaluator;
+
+ /**
+ * Helper function for generating {@link WifiConfiguration} for testing.
+ *
+ * @param fqdn The FQDN associated with the configuration
+ * @return {@link WifiConfiguration}
+ */
+ private static WifiConfiguration generateWifiConfig(String fqdn) {
+ WifiConfiguration config = new WifiConfiguration();
+ config.FQDN = fqdn;
+ return config;
+ }
+
+ /**
+ * Helper function for generating {@link PasspointProvider} for testing.
+ *
+ * @param config The WifiConfiguration associated with the provider
+ * @return {@link PasspointProvider}
+ */
+ private static PasspointProvider generateProvider(WifiConfiguration config) {
+ PasspointProvider provider = mock(PasspointProvider.class);
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(config.FQDN);
+ passpointConfig.setHomeSp(homeSp);
+ when(provider.getConfig()).thenReturn(passpointConfig);
+ when(provider.getWifiConfig()).thenReturn(config);
+ return provider;
+ }
+
+ /**
+ * Helper function for generating {@link ScanDetail} for testing.
+ *
+ * @param ssid The SSID associated with the scan
+ * @param rssiLevel The RSSI level associated with the scan
+ * @return {@link ScanDetail}
+ */
+ private static ScanDetail generateScanDetail(String ssid) {
+ NetworkDetail networkDetail = mock(NetworkDetail.class);
+ when(networkDetail.isInterworking()).thenReturn(true);
+ when(networkDetail.getAnt()).thenReturn(NetworkDetail.Ant.FreePublic);
+
+ ScanDetail scanDetail = mock(ScanDetail.class);
+ when(scanDetail.getSSID()).thenReturn(ssid);
+ when(scanDetail.getScanResult()).thenReturn(new ScanResult());
+ when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
+ return scanDetail;
+ }
+
+ /**
+ * Test setup.
+ */
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ mLocalLog = new LocalLog(512);
+ mEvaluator = new PasspointNetworkEvaluator(mPasspointManager, mWifiConfigManager,
+ mLocalLog);
+ }
+
+ /**
+ * Verify that null will be returned when evaluating scans without any matching providers.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithNoMatch() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+ when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(null);
+ assertEquals(null, mEvaluator.evaluateNetworks(
+ scanDetails, null, null, false, false, connectableNetworks));
+ assertTrue(connectableNetworks.isEmpty());
+ }
+
+ /**
+ * Verify that provider matching will not be performed when evaluating scans with no
+ * interworking support, and null will be returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaulateScansWithNoInterworkingAP() throws Exception {
+ NetworkDetail networkDetail = mock(NetworkDetail.class);
+ when(networkDetail.isInterworking()).thenReturn(false);
+ ScanDetail scanDetail = mock(ScanDetail.class);
+ when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
+
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {scanDetail});
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+ assertEquals(null, mEvaluator.evaluateNetworks(
+ scanDetails, null, null, false, false, connectableNetworks));
+ assertTrue(connectableNetworks.isEmpty());
+ // Verify that no provider matching is performed.
+ verify(mPasspointManager, never()).matchProvider(any(ScanResult.class));
+ }
+
+ /**
+ * Verify that when a network matches a home provider is found, the correct network
+ * information (WifiConfiguration) is setup and returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithNetworkMatchingHomeProvider() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+
+ // Setup matching providers for ScanDetail with TEST_SSID1.
+ Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create(
+ TEST_PROVIDER1, PasspointMatch.HomeProvider);
+
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+
+ // Return homeProvider for the first ScanDetail (TEST_SSID1) and a null (no match) for
+ // for the second (TEST_SSID2);
+ when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(homeProvider)
+ .thenReturn(null);
+ when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+ .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
+ when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
+ assertNotNull(mEvaluator.evaluateNetworks(scanDetails, null, null, false,
+ false, connectableNetworks));
+ assertEquals(1, connectableNetworks.size());
+
+ // Verify the content of the WifiConfiguration that was added to WifiConfigManager.
+ ArgumentCaptor<WifiConfiguration> addedConfig =
+ ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiConfigManager).addOrUpdateNetwork(addedConfig.capture(), anyInt());
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID1), addedConfig.getValue().SSID);
+ assertEquals(TEST_FQDN1, addedConfig.getValue().FQDN);
+ assertTrue(addedConfig.getValue().isHomeProviderNetwork);
+ verify(mWifiConfigManager).enableNetwork(eq(TEST_NETWORK_ID), eq(false), anyInt());
+ verify(mWifiConfigManager).setNetworkCandidateScanResult(
+ eq(TEST_NETWORK_ID), any(ScanResult.class), anyInt());
+ verify(mWifiConfigManager).updateScanDetailForNetwork(
+ eq(TEST_NETWORK_ID), any(ScanDetail.class));
+ }
+
+ /**
+ * Verify that when a network matches a roaming provider is found, the correct network
+ * information (WifiConfiguration) is setup and returned.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithNetworkMatchingRoamingProvider() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+
+ // Setup matching providers for ScanDetail with TEST_SSID1.
+ Pair<PasspointProvider, PasspointMatch> roamingProvider = Pair.create(
+ TEST_PROVIDER1, PasspointMatch.RoamingProvider);
+
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+
+ // Return roamingProvider for the first ScanDetail (TEST_SSID1) and a null (no match) for
+ // for the second (TEST_SSID2);
+ when(mPasspointManager.matchProvider(any(ScanResult.class))).thenReturn(roamingProvider)
+ .thenReturn(null);
+ when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+ .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
+ when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
+ assertNotNull(mEvaluator.evaluateNetworks(scanDetails, null, null, false,
+ false, connectableNetworks));
+ assertEquals(1, connectableNetworks.size());
+
+ // Verify the content of the WifiConfiguration that was added to WifiConfigManager.
+ ArgumentCaptor<WifiConfiguration> addedConfig =
+ ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiConfigManager).addOrUpdateNetwork(addedConfig.capture(), anyInt());
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID1), addedConfig.getValue().SSID);
+ assertEquals(TEST_FQDN1, addedConfig.getValue().FQDN);
+ assertFalse(addedConfig.getValue().isHomeProviderNetwork);
+ verify(mWifiConfigManager).enableNetwork(eq(TEST_NETWORK_ID), eq(false), anyInt());
+ verify(mWifiConfigManager).setNetworkCandidateScanResult(
+ eq(TEST_NETWORK_ID), any(ScanResult.class), anyInt());
+ verify(mWifiConfigManager).updateScanDetailForNetwork(
+ eq(TEST_NETWORK_ID), any(ScanDetail.class));
+ }
+
+ /**
+ * Verify that when a network matches a home provider and another network matches a roaming
+ * provider are found, the network that matched to a home provider is preferred.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithHomeProviderNewtorkAndRoamingProviderNetwork() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+
+ // Setup matching providers for ScanDetail with TEST_SSID1.
+ Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create(
+ TEST_PROVIDER1, PasspointMatch.HomeProvider);
+ Pair<PasspointProvider, PasspointMatch> roamingProvider = Pair.create(
+ TEST_PROVIDER2, PasspointMatch.RoamingProvider);
+
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+
+ // Return homeProvider for the first ScanDetail (TEST_SSID1) and
+ // roamingProvider for the second (TEST_SSID2);
+ when(mPasspointManager.matchProvider(any(ScanResult.class)))
+ .thenReturn(homeProvider).thenReturn(roamingProvider);
+ when(mWifiConfigManager.addOrUpdateNetwork(any(WifiConfiguration.class), anyInt()))
+ .thenReturn(new NetworkUpdateResult(TEST_NETWORK_ID));
+ when(mWifiConfigManager.getConfiguredNetwork(TEST_NETWORK_ID)).thenReturn(TEST_CONFIG1);
+ assertNotNull(mEvaluator.evaluateNetworks(scanDetails, null, null, false,
+ false, connectableNetworks));
+ assertEquals(1, connectableNetworks.size());
+
+ // Verify the content of the WifiConfiguration that was added to WifiConfigManager.
+ ArgumentCaptor<WifiConfiguration> addedConfig =
+ ArgumentCaptor.forClass(WifiConfiguration.class);
+ verify(mWifiConfigManager).addOrUpdateNetwork(addedConfig.capture(), anyInt());
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID1), addedConfig.getValue().SSID);
+ assertEquals(TEST_FQDN1, addedConfig.getValue().FQDN);
+ assertTrue(addedConfig.getValue().isHomeProviderNetwork);
+ verify(mWifiConfigManager).enableNetwork(eq(TEST_NETWORK_ID), eq(false), anyInt());
+ verify(mWifiConfigManager).setNetworkCandidateScanResult(
+ eq(TEST_NETWORK_ID), any(ScanResult.class), anyInt());
+ verify(mWifiConfigManager).updateScanDetailForNetwork(
+ eq(TEST_NETWORK_ID), any(ScanDetail.class));
+ }
+
+ /**
+ * Verify that when two networks both matches a home provider, with one of them being the
+ * active network, the active network is preferred.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void evaluateScansWithActiveNetworkMatchingHomeProvider() throws Exception {
+ List<ScanDetail> scanDetails = Arrays.asList(new ScanDetail[] {
+ generateScanDetail(TEST_SSID1), generateScanDetail(TEST_SSID2)});
+
+ // Setup matching providers for both ScanDetail.
+ Pair<PasspointProvider, PasspointMatch> homeProvider = Pair.create(
+ TEST_PROVIDER1, PasspointMatch.HomeProvider);
+
+ // Setup currently connected network
+ WifiConfiguration currentNetwork = new WifiConfiguration();
+ currentNetwork.networkId = TEST_NETWORK_ID;
+ currentNetwork.SSID = ScanResultUtil.createQuotedSSID(TEST_SSID2);
+ String currentBssid = "12:23:34:45:12:0F";
+
+ // Returning the same matching provider for both ScanDetail.
+ List<Pair<ScanDetail, WifiConfiguration>> connectableNetworks = new ArrayList<>();
+ when(mPasspointManager.matchProvider(any(ScanResult.class)))
+ .thenReturn(homeProvider).thenReturn(homeProvider);
+ WifiConfiguration config = mEvaluator.evaluateNetworks(scanDetails, currentNetwork,
+ currentBssid, true, false, connectableNetworks);
+ assertEquals(1, connectableNetworks.size());
+
+ // Verify no new network is added to WifiConfigManager.
+ verify(mWifiConfigManager, never()).addOrUpdateNetwork(
+ any(WifiConfiguration.class), anyInt());
+
+ // Verify current active network is returned.
+ assertEquals(ScanResultUtil.createQuotedSSID(TEST_SSID2), config.SSID);
+ assertEquals(TEST_NETWORK_ID, config.networkId);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java
new file mode 100644
index 0000000..10c50f5
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointNetworkScoreTest.java
@@ -0,0 +1,348 @@
+/*
+ * 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.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.net.wifi.ScanResult;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.ScanDetail;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.Constants;
+import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
+import com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement;
+import com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointNetworkScore}.
+ */
+@SmallTest
+public class PasspointNetworkScoreTest {
+ private static class TestData {
+ public final boolean isHomeProvider;
+ public final boolean isActiveNetwork;
+ public final int rssiLevel;
+ public final boolean internetAccess;
+ public final NetworkDetail.Ant networkType;
+ public final Map<ANQPElementType, ANQPElement> anqpElements;
+ public int expectedScore;
+
+ TestData(boolean homeProvider, boolean activeNetwork, int rssi, boolean internet,
+ NetworkDetail.Ant type, Map<ANQPElementType, ANQPElement> elements,
+ int score) {
+ isHomeProvider = homeProvider;
+ isActiveNetwork = activeNetwork;
+ rssiLevel = rssi;
+ internetAccess = internet;
+ networkType = type;
+ anqpElements = elements;
+ expectedScore = score;
+ }
+ }
+
+ private static final HSWanMetricsElement WAN_PORT_DOWN_ELEMENT = new HSWanMetricsElement(
+ HSWanMetricsElement.LINK_STATUS_DOWN /* status */, true /* symmetric */,
+ false /* capped */, 1233 /* downlinkSpeed */, 1233 /* uplinkSpeed */,
+ 10 /* downlinkLoad */, 10 /* uplinkLoad */, 12 /* lmd */);
+
+ private static final HSWanMetricsElement WAN_PORT_UP_ELEMENT = new HSWanMetricsElement(
+ HSWanMetricsElement.LINK_STATUS_UP /* status */, true /* symmetric */,
+ false /* capped */, 1233 /* downlinkSpeed */, 1233 /* uplinkSpeed */,
+ 10 /* downlinkLoad */, 10 /* uplinkLoad */, 12 /* lmd */);
+
+ private static final HSWanMetricsElement WAN_PORT_CAPPED_ELEMENT = new HSWanMetricsElement(
+ HSWanMetricsElement.LINK_STATUS_UP /* status */, true /* symmetric */,
+ true /* capped */, 1233 /* downlinkSpeed */, 1233 /* uplinkSpeed */,
+ 10 /* downlinkLoad */, 10 /* uplinkLoad */, 12 /* lmd */);
+
+ private static final IPAddressTypeAvailabilityElement UNRESTRICTED_IP_ADDRESS_ELEMENT =
+ new IPAddressTypeAvailabilityElement(IPAddressTypeAvailabilityElement.IPV4_PUBLIC,
+ IPAddressTypeAvailabilityElement.IPV6_AVAILABLE);
+
+ private static final IPAddressTypeAvailabilityElement UNAVAILABLE_IP_ADDRESS_ELEMENT =
+ new IPAddressTypeAvailabilityElement(
+ IPAddressTypeAvailabilityElement.IPV4_NOT_AVAILABLE,
+ IPAddressTypeAvailabilityElement.IPV6_NOT_AVAILABLE);
+
+ private static final IPAddressTypeAvailabilityElement UNKNOWN_IP_ADDRESS_ELEMENT =
+ new IPAddressTypeAvailabilityElement(
+ IPAddressTypeAvailabilityElement.IPV4_UNKNOWN,
+ IPAddressTypeAvailabilityElement.IPV6_UNKNOWN);
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_WAN_PORT_DOWN =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_WAN_PORT_UP =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_WAN_PORT_CAPPED =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_UNRESTRICTED_IP =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_UNAVAILABLE_IP =
+ new HashMap<>();
+
+ private static final Map<ANQPElementType, ANQPElement> TEST_ANQP_WITH_UNKNOWN_IP =
+ new HashMap<>();
+
+ // List of test data.
+ private static final List<TestData> TEST_DATA_LIST = new ArrayList<>();
+ static {
+ // Setup ANQP elements map for testing.
+ TEST_ANQP_WITH_WAN_PORT_DOWN.put(Constants.ANQPElementType.HSWANMetrics,
+ WAN_PORT_DOWN_ELEMENT);
+ TEST_ANQP_WITH_WAN_PORT_UP.put(Constants.ANQPElementType.HSWANMetrics,
+ WAN_PORT_UP_ELEMENT);
+ TEST_ANQP_WITH_WAN_PORT_CAPPED.put(Constants.ANQPElementType.HSWANMetrics,
+ WAN_PORT_CAPPED_ELEMENT);
+ TEST_ANQP_WITH_UNRESTRICTED_IP.put(Constants.ANQPElementType.ANQPIPAddrAvailability,
+ UNRESTRICTED_IP_ADDRESS_ELEMENT);
+ TEST_ANQP_WITH_UNAVAILABLE_IP.put(Constants.ANQPElementType.ANQPIPAddrAvailability,
+ UNAVAILABLE_IP_ADDRESS_ELEMENT);
+ TEST_ANQP_WITH_UNKNOWN_IP.put(Constants.ANQPElementType.ANQPIPAddrAvailability,
+ UNKNOWN_IP_ADDRESS_ELEMENT);
+
+ // Home provider public network with Internet access that's not the current
+ // active network.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider public network with Internet access that's the current active network.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, true /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, true)));
+
+ // Home provider public network without Internet access that's not the current
+ // active network.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, false /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ - PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider personal network with Internet access that's not the current active
+ // network.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.Personal /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PERSONAL_OR_EMERGENCY_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating WAN port is up.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_WAN_PORT_UP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating WAN port is down.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_WAN_PORT_DOWN /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ - PasspointNetworkScore.WAN_PORT_DOWN_OR_CAPPED_PENALTY));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating WAN port is capped (max load reached).
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_WAN_PORT_CAPPED /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ - PasspointNetworkScore.WAN_PORT_DOWN_OR_CAPPED_PENALTY));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating both IPv4 and IPv6 addresses are available.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_UNRESTRICTED_IP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ + PasspointNetworkScore.UNRESTRICTED_IP_AWARDS * 2 /* one for IPv4 and IPv6 */));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating both IPv4 and IPv6 addresses are available.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_UNRESTRICTED_IP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ /* one each for IPv4 and IPv6. */
+ + PasspointNetworkScore.UNRESTRICTED_IP_AWARDS * 2));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating both IPv4 and IPv6 addresses are
+ // unavailable.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_UNAVAILABLE_IP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Home provider public network with Internet access that's not the current
+ // active network, and ANPQ element indicating both IPv4 and IPv6 addresses are unknown.
+ TEST_DATA_LIST.add(new TestData(true /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */,
+ TEST_ANQP_WITH_UNKNOWN_IP /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.HOME_PROVIDER_AWARD
+ + PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)
+ /* one each for IPv4 and IPv6. */
+ + PasspointNetworkScore.RESTRICTED_OR_UNKNOWN_IP_AWARDS * 2));
+
+ // Roaming provider public network with Internet access that's not the current active
+ // network.
+ TEST_DATA_LIST.add(new TestData(false /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Roaming provider public network with Internet access that's the current active network.
+ TEST_DATA_LIST.add(new TestData(false /* isHomeProvider */, true /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, true)));
+
+ // Roaming provider public network without Internet access that's not the current active
+ // network.
+ TEST_DATA_LIST.add(new TestData(false /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, false /* internetAccess */,
+ NetworkDetail.Ant.FreePublic /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.PUBLIC_OR_PRIVATE_NETWORK_AWARDS
+ - PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+
+ // Roaming provider personal network with Internet access that's not the current active
+ // network.
+ TEST_DATA_LIST.add(new TestData(false /* isHomeProvider */, false /* isActiveNetwork */,
+ -60 /* rssiLevel */, true /* internetAccess */,
+ NetworkDetail.Ant.Personal /* networkType */, null /* anqpElements */,
+ /* expectedScore */
+ PasspointNetworkScore.INTERNET_ACCESS_AWARD
+ + PasspointNetworkScore.PERSONAL_OR_EMERGENCY_NETWORK_AWARDS
+ + PasspointNetworkScore.RSSI_SCORE.lookupScore(-60, false)));
+ }
+
+ /**
+ * Helper function for generating a {@link ScanDetail} for testing.
+ *
+ * @param rssiLevel RSSI level of the network
+ * @param internetAccess Flag indicating if the network provides Internet access
+ * @param networkType The type of the network
+ * @return {@link ScanDetail}
+ */
+ private static ScanDetail generateScanDetail(int rssiLevel, boolean internetAccess,
+ NetworkDetail.Ant networkType) {
+ // Setup ScanResult.
+ ScanResult scanResult = new ScanResult();
+ scanResult.level = -60;
+
+ // Setup NetworkDetail.
+ NetworkDetail networkDetail = mock(NetworkDetail.class);
+ when(networkDetail.isInternet()).thenReturn(internetAccess);
+ when(networkDetail.getAnt()).thenReturn(networkType);
+
+ // Setup ScanDetail.
+ ScanDetail scanDetail = mock(ScanDetail.class);
+ when(scanDetail.getScanResult()).thenReturn(scanResult);
+ when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
+
+ return scanDetail;
+ }
+
+ /**
+ * Go through the list of the test data {@link #TEST_DATA_LIST} and verify the score for each.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void calculateScore() throws Exception {
+ for (TestData data : TEST_DATA_LIST) {
+ ScanDetail scanDetail = generateScanDetail(data.rssiLevel, data.internetAccess,
+ data.networkType);
+ assertEquals(data.expectedScore, PasspointNetworkScore.calculateScore(
+ data.isHomeProvider, scanDetail, data.anqpElements, data.isActiveNetwork));
+ }
+ }
+
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
new file mode 100644
index 0000000..c416a96
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointProviderTest.java
@@ -0,0 +1,901 @@
+/*
+ * 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.hotspot2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.net.wifi.EAPConstants;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import com.android.server.wifi.FakeKeys;
+import com.android.server.wifi.IMSIParameter;
+import com.android.server.wifi.SIMAccessor;
+import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.hotspot2.anqp.ANQPElement;
+import com.android.server.wifi.hotspot2.anqp.CellularNetwork;
+import com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType;
+import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmData;
+import com.android.server.wifi.hotspot2.anqp.NAIRealmElement;
+import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement;
+import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
+import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
+import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
+import com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointProvider}.
+ */
+@SmallTest
+public class PasspointProviderTest {
+ private static final long PROVIDER_ID = 12L;
+ private static final int CREATOR_UID = 1234;
+ private static final String CA_CERTIFICATE_NAME = "CACERT_HS2_12";
+ private static final String CLIENT_CERTIFICATE_NAME = "USRCERT_HS2_12";
+ private static final String CLIENT_PRIVATE_KEY_NAME = "USRPKEY_HS2_12";
+ private static final String CA_CERTIFICATE_ALIAS = "HS2_12";
+ private static final String CLIENT_CERTIFICATE_ALIAS = "HS2_12";
+ private static final String CLIENT_PRIVATE_KEY_ALIAS = "HS2_12";
+
+ @Mock WifiKeyStore mKeyStore;
+ @Mock SIMAccessor mSimAccessor;
+ PasspointProvider mProvider;
+
+ /** Sets up test. */
+ @Before
+ public void setUp() throws Exception {
+ initMocks(this);
+ }
+
+ /**
+ * Helper function for creating a provider instance for testing.
+ *
+ * @param config The configuration associated with the provider
+ * @return {@link com.android.server.wifi.hotspot2.PasspointProvider}
+ */
+ private PasspointProvider createProvider(PasspointConfiguration config) {
+ return new PasspointProvider(config, mKeyStore, mSimAccessor, PROVIDER_ID, CREATOR_UID);
+ }
+
+ /**
+ * Verify that the configuration associated with the provider is the same or not the same
+ * as the expected configuration.
+ *
+ * @param expectedConfig The expected configuration
+ * @param equals Flag indicating equality or inequality check
+ */
+ private void verifyInstalledConfig(PasspointConfiguration expectedConfig, boolean equals) {
+ PasspointConfiguration actualConfig = mProvider.getConfig();
+ if (equals) {
+ assertTrue(actualConfig.equals(expectedConfig));
+ } else {
+ assertFalse(actualConfig.equals(expectedConfig));
+ }
+ }
+
+ /**
+ * Helper function for creating a Domain Name ANQP element.
+ *
+ * @param domains List of domain names
+ * @return {@link DomainNameElement}
+ */
+ private DomainNameElement createDomainNameElement(String[] domains) {
+ return new DomainNameElement(Arrays.asList(domains));
+ }
+
+ /**
+ * Helper function for creating a NAI Realm ANQP element.
+ *
+ * @param realm The realm of the network
+ * @param eapMethodID EAP Method ID
+ * @param authParam Authentication parameter
+ * @return {@link NAIRealmElement}
+ */
+ private NAIRealmElement createNAIRealmElement(String realm, int eapMethodID,
+ AuthParam authParam) {
+ Map<Integer, Set<AuthParam>> authParamMap = new HashMap<>();
+ if (authParam != null) {
+ Set<AuthParam> authSet = new HashSet<>();
+ authSet.add(authParam);
+ authParamMap.put(authParam.getAuthTypeID(), authSet);
+ }
+ EAPMethod eapMethod = new EAPMethod(eapMethodID, authParamMap);
+ NAIRealmData realmData = new NAIRealmData(Arrays.asList(new String[] {realm}),
+ Arrays.asList(new EAPMethod[] {eapMethod}));
+ return new NAIRealmElement(Arrays.asList(new NAIRealmData[] {realmData}));
+ }
+
+ /**
+ * Helper function for creating a Roaming Consortium ANQP element.
+ *
+ * @param rcOIs Roaming consortium OIs
+ * @return {@link RoamingConsortiumElement}
+ */
+ private RoamingConsortiumElement createRoamingConsortiumElement(Long[] rcOIs) {
+ return new RoamingConsortiumElement(Arrays.asList(rcOIs));
+ }
+
+ /**
+ * Helper function for creating a 3GPP Network ANQP element.
+ *
+ * @param imsiList List of IMSI to be included in a 3GPP Network
+ * @return {@link ThreeGPPNetworkElement}
+ */
+ private ThreeGPPNetworkElement createThreeGPPNetworkElement(String[] imsiList) {
+ CellularNetwork network = new CellularNetwork(Arrays.asList(imsiList));
+ return new ThreeGPPNetworkElement(Arrays.asList(new CellularNetwork[] {network}));
+ }
+
+ /**
+ * Verify that modification to the configuration used for creating PasspointProvider
+ * will not change the configuration stored inside the PasspointProvider.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyModifyOriginalConfig() throws Exception {
+ // Create a dummy PasspointConfiguration.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test1");
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setUserCredential(new Credential.UserCredential());
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+ verifyInstalledConfig(config, true);
+
+ // Modify the original configuration, the configuration maintained by the provider
+ // should be unchanged.
+ config.getHomeSp().setFqdn("test2");
+ verifyInstalledConfig(config, false);
+ }
+
+ /**
+ * Verify that modification to the configuration retrieved from the PasspointProvider
+ * will not change the configuration stored inside the PasspointProvider.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void verifyModifyRetrievedConfig() throws Exception {
+ // Create a dummy PasspointConfiguration.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("test1");
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setUserCredential(new Credential.UserCredential());
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+ verifyInstalledConfig(config, true);
+
+ // Modify the retrieved configuration, verify the configuration maintained by the
+ // provider should be unchanged.
+ PasspointConfiguration retrievedConfig = mProvider.getConfig();
+ retrievedConfig.getHomeSp().setFqdn("test2");
+ verifyInstalledConfig(retrievedConfig, false);
+ }
+
+ /**
+ * Verify a successful installation of certificates and key.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void installCertsAndKeysSuccess() throws Exception {
+ // Create a dummy configuration with certificate credential.
+ PasspointConfiguration config = new PasspointConfiguration();
+ Credential credential = new Credential();
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertSha256Fingerprint(
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
+ credential.setCertCredential(certCredential);
+ credential.setCaCertificate(FakeKeys.CA_CERT0);
+ credential.setClientPrivateKey(FakeKeys.RSA_KEY1);
+ credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT});
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Install client certificate and key to the keystore successfully.
+ when(mKeyStore.putCertInKeyStore(CA_CERTIFICATE_NAME, FakeKeys.CA_CERT0))
+ .thenReturn(true);
+ when(mKeyStore.putKeyInKeyStore(CLIENT_PRIVATE_KEY_NAME, FakeKeys.RSA_KEY1))
+ .thenReturn(true);
+ when(mKeyStore.putCertInKeyStore(CLIENT_CERTIFICATE_NAME, FakeKeys.CLIENT_CERT))
+ .thenReturn(true);
+ assertTrue(mProvider.installCertsAndKeys());
+
+ // Verify client certificate and key in the configuration gets cleared and aliases
+ // are set correctly.
+ PasspointConfiguration curConfig = mProvider.getConfig();
+ assertTrue(curConfig.getCredential().getCaCertificate() == null);
+ assertTrue(curConfig.getCredential().getClientPrivateKey() == null);
+ assertTrue(curConfig.getCredential().getClientCertificateChain() == null);
+ assertTrue(mProvider.getCaCertificateAlias().equals(CA_CERTIFICATE_ALIAS));
+ assertTrue(mProvider.getClientPrivateKeyAlias().equals(CLIENT_PRIVATE_KEY_ALIAS));
+ assertTrue(mProvider.getClientCertificateAlias().equals(CLIENT_CERTIFICATE_ALIAS));
+ }
+
+ /**
+ * Verify a failure installation of certificates and key.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void installCertsAndKeysFailure() throws Exception {
+ // Create a dummy configuration with certificate credential.
+ PasspointConfiguration config = new PasspointConfiguration();
+ Credential credential = new Credential();
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertSha256Fingerprint(
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
+ credential.setCertCredential(certCredential);
+ credential.setCaCertificate(FakeKeys.CA_CERT0);
+ credential.setClientPrivateKey(FakeKeys.RSA_KEY1);
+ credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT});
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Failed to install client certificate to the keystore.
+ when(mKeyStore.putCertInKeyStore(CA_CERTIFICATE_NAME, FakeKeys.CA_CERT0))
+ .thenReturn(true);
+ when(mKeyStore.putKeyInKeyStore(CLIENT_PRIVATE_KEY_NAME, FakeKeys.RSA_KEY1))
+ .thenReturn(true);
+ when(mKeyStore.putCertInKeyStore(CLIENT_CERTIFICATE_NAME, FakeKeys.CLIENT_CERT))
+ .thenReturn(false);
+ assertFalse(mProvider.installCertsAndKeys());
+
+ // Verify certificates and key in the configuration are not cleared and aliases
+ // are not set.
+ PasspointConfiguration curConfig = mProvider.getConfig();
+ assertTrue(curConfig.getCredential().getCaCertificate() != null);
+ assertTrue(curConfig.getCredential().getClientCertificateChain() != null);
+ assertTrue(curConfig.getCredential().getClientPrivateKey() != null);
+ assertTrue(mProvider.getCaCertificateAlias() == null);
+ assertTrue(mProvider.getClientPrivateKeyAlias() == null);
+ assertTrue(mProvider.getClientCertificateAlias() == null);
+ }
+
+ /**
+ * Verify a successful uninstallation of certificates and key.
+ */
+ @Test
+ public void uninstallCertsAndKeys() throws Exception {
+ // Create a dummy configuration with certificate credential.
+ PasspointConfiguration config = new PasspointConfiguration();
+ Credential credential = new Credential();
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertSha256Fingerprint(
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
+ credential.setCertCredential(certCredential);
+ credential.setCaCertificate(FakeKeys.CA_CERT0);
+ credential.setClientPrivateKey(FakeKeys.RSA_KEY1);
+ credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT});
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Install client certificate and key to the keystore successfully.
+ when(mKeyStore.putCertInKeyStore(CA_CERTIFICATE_NAME, FakeKeys.CA_CERT0))
+ .thenReturn(true);
+ when(mKeyStore.putKeyInKeyStore(CLIENT_PRIVATE_KEY_NAME, FakeKeys.RSA_KEY1))
+ .thenReturn(true);
+ when(mKeyStore.putCertInKeyStore(CLIENT_CERTIFICATE_NAME, FakeKeys.CLIENT_CERT))
+ .thenReturn(true);
+ assertTrue(mProvider.installCertsAndKeys());
+ assertTrue(mProvider.getCaCertificateAlias().equals(CA_CERTIFICATE_ALIAS));
+ assertTrue(mProvider.getClientPrivateKeyAlias().equals(CLIENT_PRIVATE_KEY_ALIAS));
+ assertTrue(mProvider.getClientCertificateAlias().equals(CLIENT_CERTIFICATE_ALIAS));
+
+ // Uninstall certificates and key from the keystore.
+ mProvider.uninstallCertsAndKeys();
+ verify(mKeyStore).removeEntryFromKeyStore(CA_CERTIFICATE_NAME);
+ verify(mKeyStore).removeEntryFromKeyStore(CLIENT_CERTIFICATE_NAME);
+ verify(mKeyStore).removeEntryFromKeyStore(CLIENT_PRIVATE_KEY_NAME);
+ assertTrue(mProvider.getCaCertificateAlias() == null);
+ assertTrue(mProvider.getClientPrivateKeyAlias() == null);
+ assertTrue(mProvider.getClientCertificateAlias() == null);
+ }
+
+ /**
+ * Verify that a provider is a home provider when its FQDN matches a domain name in the
+ * Domain Name ANQP element and no NAI realm is provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchFQDNWithoutNAIRealm() throws Exception {
+ String testDomain = "test.com";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(testDomain);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2);
+ credential.setUserCredential(userCredential);
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Setup ANQP elements.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {testDomain}));
+
+ assertEquals(PasspointMatch.HomeProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a home provider when its FQDN matches a domain name in the
+ * Domain Name ANQP element and the provider's credential matches the NAI realm provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchFQDNWithNAIRealmMatch() throws Exception {
+ String testDomain = "test.com";
+ String testRealm = "realm.com";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(testDomain);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setRealm(testRealm);
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2);
+ credential.setUserCredential(userCredential);
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Setup Domain Name ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {testDomain}));
+ anqpElementMap.put(ANQPElementType.ANQPNAIRealm,
+ createNAIRealmElement(testRealm, EAPConstants.EAP_TTLS,
+ new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAPV2)));
+
+ assertEquals(PasspointMatch.HomeProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that there is no match when the provider's FQDN matches a domain name in the
+ * Domain Name ANQP element but the provider's credential doesn't match the authentication
+ * method provided in the NAI realm.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchFQDNWithNAIRealmMismatch() throws Exception {
+ String testDomain = "test.com";
+ String testRealm = "realm.com";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(testDomain);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setRealm(testRealm);
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2);
+ credential.setUserCredential(userCredential);
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Setup Domain Name ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {testDomain}));
+ anqpElementMap.put(ANQPElementType.ANQPNAIRealm,
+ createNAIRealmElement(testRealm, EAPConstants.EAP_TLS, null));
+
+ assertEquals(PasspointMatch.None, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a home provider when its SIM credential matches an 3GPP network
+ * domain name in the Domain Name ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void match3GPPNetworkDomainName() throws Exception {
+ String testImsi = "1234567890";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.setHomeSp(new HomeSp());
+ Credential credential = new Credential();
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi(testImsi);
+ credential.setSimCredential(simCredential);
+ config.setCredential(credential);
+ when(mSimAccessor.getMatchingImsis(new IMSIParameter(testImsi, false)))
+ .thenReturn(Arrays.asList(new String[] {testImsi}));
+ mProvider = createProvider(config);
+
+ // Setup Domain Name ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {"wlan.mnc456.mcc123.3gppnetwork.org"}));
+
+ assertEquals(PasspointMatch.HomeProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a roaming provider when a roaming consortium OI matches an OI
+ * in the roaming consortium ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchRoamingConsortium() throws Exception {
+ long[] providerRCOIs = new long[] {0x1234L, 0x2345L};
+ Long[] anqpRCOIs = new Long[] {0x1234L, 0x2133L};
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setRoamingConsortiumOis(providerRCOIs);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2);
+ credential.setUserCredential(userCredential);
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Setup Roaming Consortium ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium,
+ createRoamingConsortiumElement(anqpRCOIs));
+
+ assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a roaming provider when the provider's IMSI parameter and an
+ * IMSI from the SIM card matches a MCC-MNC in the 3GPP Network ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchThreeGPPNetwork() throws Exception {
+ String testImsi = "1234567890";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.setHomeSp(new HomeSp());
+ Credential credential = new Credential();
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi(testImsi);
+ credential.setSimCredential(simCredential);
+ config.setCredential(credential);
+ when(mSimAccessor.getMatchingImsis(new IMSIParameter(testImsi, false)))
+ .thenReturn(Arrays.asList(new String[] {testImsi}));
+ mProvider = createProvider(config);
+
+ // Setup 3GPP Network ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQP3GPPNetwork,
+ createThreeGPPNetworkElement(new String[] {"123456"}));
+
+ assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a roaming provider when its credential matches a NAI realm in
+ * the NAI Realm ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchNAIRealm() throws Exception {
+ String testRealm = "realm.com";
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.setHomeSp(new HomeSp());
+ Credential credential = new Credential();
+ credential.setRealm(testRealm);
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2);
+ credential.setUserCredential(userCredential);
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Setup NAI Realm ANQP element.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPNAIRealm,
+ createNAIRealmElement(testRealm, EAPConstants.EAP_TTLS,
+ new NonEAPInnerAuth(NonEAPInnerAuth.AUTH_TYPE_MSCHAPV2)));
+
+ assertEquals(PasspointMatch.RoamingProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that a provider is a home provider when its FQDN, roaming consortium OI, and
+ * IMSI all matched against the ANQP elements, since we prefer matching home provider over
+ * roaming provider.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void matchHomeOverRoamingProvider() throws Exception {
+ // Setup test data.
+ String testDomain = "test.com";
+ String testImsi = "1234567890";
+ long[] providerRCOIs = new long[] {0x1234L, 0x2345L};
+ Long[] anqpRCOIs = new Long[] {0x1234L, 0x2133L};
+
+ // Setup test provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(testDomain);
+ homeSp.setRoamingConsortiumOis(providerRCOIs);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi(testImsi);
+ credential.setSimCredential(simCredential);
+ config.setCredential(credential);
+ when(mSimAccessor.getMatchingImsis(new IMSIParameter(testImsi, false)))
+ .thenReturn(Arrays.asList(new String[] {testImsi}));
+ mProvider = createProvider(config);
+
+ // Setup ANQP elements.
+ Map<ANQPElementType, ANQPElement> anqpElementMap = new HashMap<>();
+ anqpElementMap.put(ANQPElementType.ANQPDomName,
+ createDomainNameElement(new String[] {testDomain}));
+ anqpElementMap.put(ANQPElementType.ANQPRoamingConsortium,
+ createRoamingConsortiumElement(anqpRCOIs));
+ anqpElementMap.put(ANQPElementType.ANQP3GPPNetwork,
+ createThreeGPPNetworkElement(new String[] {"123456"}));
+
+ assertEquals(PasspointMatch.HomeProvider, mProvider.match(anqpElementMap));
+ }
+
+ /**
+ * Verify that an expected WifiConfiguration will be returned for a Passpoint provider
+ * with an user credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getWifiConfigWithUserCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String username = "username";
+ String password = "password";
+ byte[] base64EncodedPw =
+ Base64.encode(password.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
+ String encodedPasswordStr = new String(base64EncodedPw, StandardCharsets.UTF_8);
+
+ // Create provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setRealm(realm);
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setUsername(username);
+ userCredential.setPassword(encodedPasswordStr);
+ userCredential.setNonEapInnerMethod(Credential.UserCredential.AUTH_METHOD_MSCHAPV2);
+ credential.setUserCredential(userCredential);
+ credential.setCaCertificate(FakeKeys.CA_CERT0);
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Install certificate.
+ when(mKeyStore.putCertInKeyStore(CA_CERTIFICATE_NAME, FakeKeys.CA_CERT0))
+ .thenReturn(true);
+ assertTrue(mProvider.installCertsAndKeys());
+
+ // Retrieve the WifiConfiguration associated with the provider, and verify the content of
+ // the configuration. Need to verify field by field since WifiConfiguration doesn't
+ // override equals() function.
+ WifiConfiguration wifiConfig = mProvider.getWifiConfig();
+ WifiEnterpriseConfig wifiEnterpriseConfig = wifiConfig.enterpriseConfig;
+ assertEquals(fqdn, wifiConfig.FQDN);
+ assertEquals(friendlyName, wifiConfig.providerFriendlyName);
+ assertTrue(Arrays.equals(rcOIs, wifiConfig.roamingConsortiumIds));
+ assertTrue(wifiConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertTrue(wifiConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertEquals(realm, wifiEnterpriseConfig.getRealm());
+ assertEquals(fqdn, wifiEnterpriseConfig.getDomainSuffixMatch());
+ assertEquals("anonymous@" + realm, wifiEnterpriseConfig.getAnonymousIdentity());
+ assertEquals(WifiEnterpriseConfig.Eap.TTLS, wifiEnterpriseConfig.getEapMethod());
+ assertEquals(WifiEnterpriseConfig.Phase2.MSCHAPV2, wifiEnterpriseConfig.getPhase2Method());
+ assertEquals(username, wifiEnterpriseConfig.getIdentity());
+ assertEquals(password, wifiEnterpriseConfig.getPassword());
+ assertEquals(CA_CERTIFICATE_ALIAS, wifiEnterpriseConfig.getCaCertificateAlias());
+ }
+
+ /**
+ * Verify that an expected WifiConfiguration will be returned for a Passpoint provider
+ * with a certificate credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getWifiConfigWithCertCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+
+ // Create provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setRealm(realm);
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertSha256Fingerprint(
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
+ credential.setCertCredential(certCredential);
+ credential.setCaCertificate(FakeKeys.CA_CERT0);
+ credential.setClientPrivateKey(FakeKeys.RSA_KEY1);
+ credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT});
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Install certificate.
+ when(mKeyStore.putCertInKeyStore(CA_CERTIFICATE_NAME, FakeKeys.CA_CERT0))
+ .thenReturn(true);
+ when(mKeyStore.putKeyInKeyStore(CLIENT_PRIVATE_KEY_NAME, FakeKeys.RSA_KEY1))
+ .thenReturn(true);
+ when(mKeyStore.putCertInKeyStore(CLIENT_CERTIFICATE_NAME, FakeKeys.CLIENT_CERT))
+ .thenReturn(true);
+ assertTrue(mProvider.installCertsAndKeys());
+
+ // Retrieve the WifiConfiguration associated with the provider, and verify the content of
+ // the configuration. Need to verify field by field since WifiConfiguration doesn't
+ // override equals() function.
+ WifiConfiguration wifiConfig = mProvider.getWifiConfig();
+ WifiEnterpriseConfig wifiEnterpriseConfig = wifiConfig.enterpriseConfig;
+ assertEquals(fqdn, wifiConfig.FQDN);
+ assertEquals(friendlyName, wifiConfig.providerFriendlyName);
+ assertTrue(Arrays.equals(rcOIs, wifiConfig.roamingConsortiumIds));
+ assertTrue(wifiConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertTrue(wifiConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertEquals(realm, wifiEnterpriseConfig.getRealm());
+ assertEquals(fqdn, wifiEnterpriseConfig.getDomainSuffixMatch());
+ assertEquals("anonymous@" + realm, wifiEnterpriseConfig.getAnonymousIdentity());
+ assertEquals(WifiEnterpriseConfig.Eap.TLS, wifiEnterpriseConfig.getEapMethod());
+ assertEquals(CLIENT_CERTIFICATE_ALIAS, wifiEnterpriseConfig.getClientCertificateAlias());
+ assertEquals(CA_CERTIFICATE_ALIAS, wifiEnterpriseConfig.getCaCertificateAlias());
+ }
+
+ /**
+ * Verify that an expected WifiConfiguration will be returned for a Passpoint provider
+ * with a SIM credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getWifiConfigWithSimCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String imsi = "1234*";
+
+ // Create provider.
+ PasspointConfiguration config = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ config.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ credential.setRealm(realm);
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi(imsi);
+ simCredential.setEapType(EAPConstants.EAP_SIM);
+ credential.setSimCredential(simCredential);
+ config.setCredential(credential);
+ mProvider = createProvider(config);
+
+ // Retrieve the WifiConfiguration associated with the provider, and verify the content of
+ // the configuration. Need to verify field by field since WifiConfiguration doesn't
+ // override equals() function.
+ WifiConfiguration wifiConfig = mProvider.getWifiConfig();
+ WifiEnterpriseConfig wifiEnterpriseConfig = wifiConfig.enterpriseConfig;
+ assertEquals(fqdn, wifiConfig.FQDN);
+ assertEquals(friendlyName, wifiConfig.providerFriendlyName);
+ assertTrue(Arrays.equals(rcOIs, wifiConfig.roamingConsortiumIds));
+ assertTrue(wifiConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertTrue(wifiConfig.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ assertEquals(realm, wifiEnterpriseConfig.getRealm());
+ assertEquals(fqdn, wifiEnterpriseConfig.getDomainSuffixMatch());
+ assertEquals(WifiEnterpriseConfig.Eap.SIM, wifiEnterpriseConfig.getEapMethod());
+ assertEquals(imsi, wifiEnterpriseConfig.getPlmn());
+ }
+
+ /**
+ * Verify that an expected {@link PasspointConfiguration} will be returned when converting
+ * from a {@link WifiConfiguration} containing an user credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void convertFromWifiConfigWithUserCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String username = "username";
+ String password = "password";
+ byte[] base64EncodedPw =
+ Base64.encode(password.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
+ String encodedPasswordStr = new String(base64EncodedPw, StandardCharsets.UTF_8);
+
+ // Setup WifiConfiguration for legacy Passpoint configuraiton.
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = friendlyName;
+ wifiConfig.roamingConsortiumIds = rcOIs;
+ wifiConfig.enterpriseConfig.setIdentity(username);
+ wifiConfig.enterpriseConfig.setPassword(password);
+ wifiConfig.enterpriseConfig.setRealm(realm);
+ wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TTLS);
+ wifiConfig.enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.PAP);
+
+ // Setup expected {@link PasspointConfiguration}
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ passpointConfig.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setUsername(username);
+ userCredential.setPassword(encodedPasswordStr);
+ userCredential.setEapType(EAPConstants.EAP_TTLS);
+ userCredential.setNonEapInnerMethod("PAP");
+ credential.setUserCredential(userCredential);
+ credential.setRealm(realm);
+ passpointConfig.setCredential(credential);
+
+ assertEquals(passpointConfig, PasspointProvider.convertFromWifiConfig(wifiConfig));
+ }
+
+ /**
+ * Verify that an expected {@link PasspointConfiguration} will be returned when converting
+ * from a {@link WifiConfiguration} containing a SIM credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void convertFromWifiConfigWithSimCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+ String imsi = "1234";
+
+ // Setup WifiConfiguration for legacy Passpoint configuraiton.
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = friendlyName;
+ wifiConfig.roamingConsortiumIds = rcOIs;
+ wifiConfig.enterpriseConfig.setRealm(realm);
+ wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM);
+ wifiConfig.enterpriseConfig.setPlmn(imsi);
+
+ // Setup expected {@link PasspointConfiguration}
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ passpointConfig.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setEapType(EAPConstants.EAP_SIM);
+ simCredential.setImsi(imsi);
+ credential.setSimCredential(simCredential);
+ credential.setRealm(realm);
+ passpointConfig.setCredential(credential);
+
+ assertEquals(passpointConfig, PasspointProvider.convertFromWifiConfig(wifiConfig));
+ }
+
+ /**
+ * Verify that an expected {@link PasspointConfiguration} will be returned when converting
+ * from a {@link WifiConfiguration} containing a certificate credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void convertFromWifiConfigWithCertCredential() throws Exception {
+ // Test data.
+ String fqdn = "test.com";
+ String friendlyName = "Friendly Name";
+ long[] rcOIs = new long[] {0x1234L, 0x2345L};
+ String realm = "realm.com";
+
+ // Setup WifiConfiguration for legacy Passpoint configuraiton.
+ WifiConfiguration wifiConfig = new WifiConfiguration();
+ wifiConfig.FQDN = fqdn;
+ wifiConfig.providerFriendlyName = friendlyName;
+ wifiConfig.roamingConsortiumIds = rcOIs;
+ wifiConfig.enterpriseConfig.setRealm(realm);
+ wifiConfig.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
+
+ // Setup expected {@link PasspointConfiguration}
+ PasspointConfiguration passpointConfig = new PasspointConfiguration();
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn(fqdn);
+ homeSp.setFriendlyName(friendlyName);
+ homeSp.setRoamingConsortiumOis(rcOIs);
+ passpointConfig.setHomeSp(homeSp);
+ Credential credential = new Credential();
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertType(Credential.CertificateCredential.CERT_TYPE_X509V3);
+ credential.setCertCredential(certCredential);
+ credential.setRealm(realm);
+ passpointConfig.setCredential(credential);
+
+ assertEquals(passpointConfig, PasspointProvider.convertFromWifiConfig(wifiConfig));
+ }
+
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointXmlUtilsTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointXmlUtilsTest.java
new file mode 100644
index 0000000..67f2dcd
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointXmlUtilsTest.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.hotspot2;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.net.wifi.hotspot2.pps.Policy;
+import android.net.wifi.hotspot2.pps.UpdateParameter;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+
+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.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.PasspointXmlUtilsTest}.
+ */
+@SmallTest
+public class PasspointXmlUtilsTest {
+
+ /**
+ * Helper function for generating a {@link PasspointConfiguration} for testing the XML
+ * serialization/deserialization logic.
+ *
+ * @return {@link PasspointConfiguration}
+ * @throws Exception
+ */
+ private PasspointConfiguration createFullPasspointConfiguration() throws Exception {
+ DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ byte[] certFingerprint = new byte[32];
+ Arrays.fill(certFingerprint, (byte) 0x1f);
+
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.setUpdateIdentifier(12);
+ config.setCredentialPriority(99);
+
+ // AAA Server trust root.
+ Map<String, byte[]> trustRootCertList = new HashMap<>();
+ trustRootCertList.put("server1.trust.root.com", certFingerprint);
+ config.setTrustRootCertList(trustRootCertList);
+
+ // Subscription update.
+ UpdateParameter subscriptionUpdate = new UpdateParameter();
+ subscriptionUpdate.setUpdateIntervalInMinutes(120);
+ subscriptionUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_SSP);
+ subscriptionUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER);
+ subscriptionUpdate.setServerUri("subscription.update.com");
+ subscriptionUpdate.setUsername("subscriptionUser");
+ subscriptionUpdate.setBase64EncodedPassword("subscriptionPass");
+ subscriptionUpdate.setTrustRootCertUrl("subscription.update.cert.com");
+ subscriptionUpdate.setTrustRootCertSha256Fingerprint(certFingerprint);
+ config.setSubscriptionUpdate(subscriptionUpdate);
+
+ // Subscription parameters.
+ config.setSubscriptionCreationTimeInMillis(format.parse("2016-02-01T10:00:00Z").getTime());
+ config.setSubscriptionExpirationTimeInMillis(
+ format.parse("2016-03-01T10:00:00Z").getTime());
+ config.setSubscriptionType("Gold");
+ config.setUsageLimitDataLimit(921890);
+ config.setUsageLimitStartTimeInMillis(format.parse("2016-12-01T10:00:00Z").getTime());
+ config.setUsageLimitTimeLimitInMinutes(120);
+ config.setUsageLimitUsageTimePeriodInMinutes(99910);
+
+ // HomeSP configuration.
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFriendlyName("Century House");
+ homeSp.setFqdn("mi6.co.uk");
+ homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L});
+ homeSp.setIconUrl("icon.test.com");
+ Map<String, Long> homeNetworkIds = new HashMap<>();
+ homeNetworkIds.put("TestSSID", 0x12345678L);
+ homeNetworkIds.put("NullHESSID", null);
+ homeSp.setHomeNetworkIds(homeNetworkIds);
+ homeSp.setMatchAllOis(new long[] {0x11223344});
+ homeSp.setMatchAnyOis(new long[] {0x55667788});
+ homeSp.setOtherHomePartners(new String[] {"other.fqdn.com"});
+ config.setHomeSp(homeSp);
+
+ // Credential configuration.
+ Credential credential = new Credential();
+ credential.setCreationTimeInMillis(format.parse("2016-01-01T10:00:00Z").getTime());
+ credential.setExpirationTimeInMillis(format.parse("2016-02-01T10:00:00Z").getTime());
+ credential.setRealm("shaken.stirred.com");
+ credential.setCheckAaaServerCertStatus(true);
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setUsername("james");
+ userCredential.setPassword("Ym9uZDAwNw==");
+ userCredential.setMachineManaged(true);
+ userCredential.setSoftTokenApp("TestApp");
+ userCredential.setAbleToShare(true);
+ userCredential.setEapType(21);
+ userCredential.setNonEapInnerMethod("MS-CHAP-V2");
+ credential.setUserCredential(userCredential);
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertType("x509v3");
+ certCredential.setCertSha256Fingerprint(certFingerprint);
+ credential.setCertCredential(certCredential);
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi("imsi");
+ simCredential.setEapType(24);
+ credential.setSimCredential(simCredential);
+ config.setCredential(credential);
+
+ // Policy configuration.
+ Policy policy = new Policy();
+ List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>();
+ Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+ partner1.setFqdn("test1.fqdn.com");
+ partner1.setFqdnExactMatch(true);
+ partner1.setPriority(127);
+ partner1.setCountries("us,fr");
+ Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+ partner2.setFqdn("test2.fqdn.com");
+ partner2.setFqdnExactMatch(false);
+ partner2.setPriority(200);
+ partner2.setCountries("*");
+ preferredRoamingPartnerList.add(partner1);
+ preferredRoamingPartnerList.add(partner2);
+ policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList);
+ policy.setMinHomeDownlinkBandwidth(23412);
+ policy.setMinHomeUplinkBandwidth(9823);
+ policy.setMinRoamingDownlinkBandwidth(9271);
+ policy.setMinRoamingUplinkBandwidth(2315);
+ policy.setExcludedSsidList(new String[] {"excludeSSID"});
+ Map<Integer, String> requiredProtoPortMap = new HashMap<>();
+ requiredProtoPortMap.put(12, "34,92,234");
+ policy.setRequiredProtoPortMap(requiredProtoPortMap);
+ policy.setMaximumBssLoadValue(23);
+ UpdateParameter policyUpdate = new UpdateParameter();
+ policyUpdate.setUpdateIntervalInMinutes(120);
+ policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM);
+ policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP);
+ policyUpdate.setServerUri("policy.update.com");
+ policyUpdate.setUsername("updateUser");
+ policyUpdate.setBase64EncodedPassword("updatePass");
+ policyUpdate.setTrustRootCertUrl("update.cert.com");
+ policyUpdate.setTrustRootCertSha256Fingerprint(certFingerprint);
+ policy.setPolicyUpdate(policyUpdate);
+ config.setPolicy(policy);
+ return config;
+ }
+
+ /**
+ * Verify the serialization and deserialization logic of a {@link PasspointConfiguration}.
+ *
+ * 1. Serialize the test config to a XML block
+ * 2. Deserialize the XML block to a {@link PasspointConfiguration}
+ * 3. Verify that the deserialized config is the same as the test config
+ *
+ * @param testConfig The configuration to used for testing
+ * @throws Exception
+ */
+ private void serializeAndDeserializePasspointConfiguration(PasspointConfiguration testConfig)
+ throws Exception {
+ final XmlSerializer out = new FastXmlSerializer();
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ out.setOutput(outputStream, StandardCharsets.UTF_8.name());
+ PasspointXmlUtils.serializePasspointConfiguration(out, testConfig);
+ out.flush();
+
+ final XmlPullParser in = Xml.newPullParser();
+ final ByteArrayInputStream inputStream =
+ new ByteArrayInputStream(outputStream.toByteArray());
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+
+ PasspointConfiguration deserializedConfig =
+ PasspointXmlUtils.deserializePasspointConfiguration(in, in.getDepth());
+ assertEquals(testConfig, deserializedConfig);
+ }
+
+ /**
+ * Verify that the serialization and deserialization logic for a full
+ * {@link PasspointConfiguration} (all fields are set) works as expected.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void serializeAndDeserializeFullPasspointConfiguration() throws Exception {
+ serializeAndDeserializePasspointConfiguration(createFullPasspointConfiguration());
+ }
+
+ /**
+ * Verify that the serialization and deserialization logic for an empty
+ * {@link PasspointConfiguration} works as expected.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void serializeAndDeserializeEmptyPasspointConfiguration() throws Exception {
+ serializeAndDeserializePasspointConfiguration(new PasspointConfiguration());
+ }
+
+ /**
+ * Verify that a XmlPullParserException will be thrown when deserialize a XML block
+ * for a PasspointConfiguraiton containing an unknown tag.
+ *
+ * @throws Exception
+ */
+ @Test(expected = XmlPullParserException.class)
+ public void deserializePasspointConfigurationWithUnknownTag() throws Exception {
+ String xmlStr = "<UnknownTag>\n"
+ + "</UnknownTag>\n";
+ final XmlPullParser in = Xml.newPullParser();
+ final ByteArrayInputStream inputStream =
+ new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8));
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ PasspointXmlUtils.deserializePasspointConfiguration(in, in.getDepth());
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java
new file mode 100644
index 0000000..8f019e0
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ANQPParserTest.java
@@ -0,0 +1,485 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.ANQPParser}.
+ */
+@SmallTest
+public class ANQPParserTest {
+ /**
+ * Helper function for generating payload for a Venue Name ANQP element.
+ *
+ * @param language Array of languages
+ * @param text Array of text
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getVenueNamePayload(String[] language, String[] text)
+ throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ stream.write(new byte[VenueNameElement.VENUE_INFO_LENGTH]);
+ stream.write(getI18NameListPayload(language, text));
+ return stream.toByteArray();
+ }
+
+ /**
+ * Helper function for generating payload for a Domain Name ANQP element.
+ *
+ * @param names Array of domain names
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getDomainNamePayload(String[] names) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ for (String name : names) {
+ byte[] nameBytes = name.getBytes(StandardCharsets.ISO_8859_1);
+ stream.write((byte) nameBytes.length);
+ stream.write(nameBytes);
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Helper function for generating payload for a Roaming Consortium ANQP element.
+ *
+ * @param ois Array of OIs
+ * @param oisLength Array of length of each corresponding OI
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getRoamingConsortiumPayload(Long[] ois, int[] oisLength)
+ throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ for (int i = 0; i < ois.length; i++) {
+ stream.write((byte) oisLength[i]);
+ // Write the OI data in big-endian.
+ for (int l = oisLength[i] - 1; l >= 0; l--) {
+ stream.write((byte) ((ois[i].longValue() >> l * Byte.SIZE) & 0xFF));
+ }
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Helper function for generating payload for a NAI Realm ANQP element.
+ *
+ * @param realmDataList Array of realm data.
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getNAIRealmPayload(byte[][] realmDataList) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ // Data count in little-endian
+ stream.write((byte) (realmDataList.length & 0xFF));
+ stream.write((byte) ((realmDataList.length >> 8) & 0xFF));
+ for (byte[] realmData : realmDataList) {
+ stream.write(realmData);
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Helper function for generating payload for 3GPP Network ANQP element.
+ *
+ * @param ieiList Array of IEI data
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getThreeGPPNetworkPayload(byte[][] ieiList) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ int totalIeiSize = CellularNetworkTestUtil.getDataSize(ieiList);
+ stream.write((byte) ThreeGPPNetworkElement.GUD_VERSION_1);
+ stream.write((byte) totalIeiSize);
+ for (byte[] iei : ieiList) {
+ stream.write(iei);
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Helper function for generating payload for Vendor Specific ANQP element.
+ *
+ * @param oi The OI of the vendor
+ * @param type The type of the element
+ * @param subtype The subtype of the element
+ * @param payload The vendor specific data
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getVendorSpecificPayload(int oi, int type, int subtype, byte[] payload)
+ throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ stream.write((byte) ((oi >> 16) & 0xFF));
+ stream.write((byte) ((oi >> 8) & 0xFF));
+ stream.write((byte) (oi & 0xFF));
+ stream.write((byte) type);
+ stream.write((byte) subtype);
+ stream.write((byte) 0); // Reserved
+ stream.write(payload);
+ return stream.toByteArray();
+ }
+
+ /**
+ * Helper function for generating payload for a Hotspot 2.0 Operator Friendly Name ANQP element.
+ *
+ * @param language Array of language
+ * @param text Array of text
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getHSFriendlyNamePayload(String[] language, String[] text)
+ throws IOException {
+ return getI18NameListPayload(language, text);
+ }
+
+ /**
+ * Helper function for generating payload for a Hotspot 2.0 WAN Metrics ANQP element.
+ *
+ * @param status Link status
+ * @param symmetric Flag indicating symmetric link speed
+ * @param capped Flag indicating link operating at max capacity
+ * @param downlinkSpeed Downlink speed
+ * @param uplinkSpeed Uplink speed
+ * @param downlinkLoad Downlink load
+ * @param uplinkLoad Uplink load
+ * @param lmd Load measurement duration
+ * @return byte[]
+ */
+ private static byte[] getHSWanMetricsPayload(int status, boolean symmetric, boolean capped,
+ long downlinkSpeed, long uplinkSpeed, int downlinkLoad, int uplinkLoad, int lmd) {
+ ByteBuffer buffer = ByteBuffer.allocate(HSWanMetricsElement.EXPECTED_BUFFER_SIZE)
+ .order(ByteOrder.LITTLE_ENDIAN);
+ int wanInfo = status & HSWanMetricsElement.LINK_STATUS_MASK;
+ if (symmetric) wanInfo |= HSWanMetricsElement.SYMMETRIC_LINK_MASK;
+ if (capped) wanInfo |= HSWanMetricsElement.AT_CAPACITY_MASK;
+ buffer.put((byte) wanInfo);
+ buffer.putInt((int) (downlinkSpeed & 0xFFFFFFFFL));
+ buffer.putInt((int) (uplinkSpeed & 0xFFFFFFFFL));
+ buffer.put((byte) (downlinkLoad & 0xFF));
+ buffer.put((byte) (uplinkLoad & 0xFF));
+ buffer.putShort((short) (lmd & 0xFFFF));
+ buffer.position(0);
+ byte[] data = new byte[HSWanMetricsElement.EXPECTED_BUFFER_SIZE];
+ buffer.get(data);
+ return data;
+ }
+
+ /**
+ * Helper function for generating payload for a Hotspot 2.0 Connection Capability ANQP
+ * element.
+ *
+ * @param protocol Network protocol
+ * @param port Network port
+ * @param status Status of the port
+ * @return byte[]
+ */
+ private static byte[] getHSConnectionCapabilityPayload(int protocol, int port, int status) {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ stream.write((byte) protocol);
+ // Write 2-byte port in little-endian.
+ stream.write((byte) (port & 0xFF));
+ stream.write((byte) ((port >> 8) & 0xFF));
+ stream.write((byte) status);
+ return stream.toByteArray();
+ }
+
+ /**
+ * Helper function for generating payload for a list of I18Name.
+ *
+ * @param language Array of language
+ * @param text Array of text
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getI18NameListPayload(String[] language, String[] text)
+ throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ for (int i = 0; i < language.length; i++) {
+ byte[] textBytes = text[i].getBytes(StandardCharsets.UTF_8);
+ int length = I18Name.LANGUAGE_CODE_LENGTH + text[i].length();
+ stream.write((byte) length);
+ stream.write(language[i].getBytes(StandardCharsets.US_ASCII));
+ // Add padding for two-character language code.
+ if (language[i].getBytes(StandardCharsets.US_ASCII).length
+ < I18Name.LANGUAGE_CODE_LENGTH) {
+ stream.write(new byte[]{(byte) 0x0});
+ }
+ stream.write(textBytes);
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Verify that an expected VenueNameElement will be returned when parsing a buffer that
+ * contained a Venue Name ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseVenueNameElement() throws Exception {
+ // Test data.
+ String[] language = new String[] {"en"};
+ String[] text = new String[] {"test"};
+
+ // Setup expectation.
+ List<I18Name> nameList = new ArrayList<>();
+ nameList.add(new I18Name(language[0], Locale.forLanguageTag(language[0]), text[0]));
+ VenueNameElement expected = new VenueNameElement(nameList);
+
+ ByteBuffer buffer = ByteBuffer.wrap(getVenueNamePayload(language, text));
+ assertEquals(expected,
+ ANQPParser.parseElement(Constants.ANQPElementType.ANQPVenueName, buffer));
+ }
+
+ /**
+ * Verify that an expected IPAddressTypeAvailabilityElement will be returned when parsing a
+ * buffer that contained an IP Address Type Availability ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseIPAddressTypeAvailabilityElement() throws Exception {
+ // Test data.
+ int ipAddressAvailability = IPAddressTypeAvailabilityElement.IPV4_PUBLIC << 2
+ | IPAddressTypeAvailabilityElement.IPV6_AVAILABLE;
+
+ // Setup expectation.
+ IPAddressTypeAvailabilityElement expected = new IPAddressTypeAvailabilityElement(
+ IPAddressTypeAvailabilityElement.IPV4_PUBLIC,
+ IPAddressTypeAvailabilityElement.IPV6_AVAILABLE);
+
+ ByteBuffer buffer = ByteBuffer.wrap(new byte[] {(byte) ipAddressAvailability});
+ assertEquals(expected,
+ ANQPParser.parseElement(Constants.ANQPElementType.ANQPIPAddrAvailability, buffer));
+ }
+
+ /**
+ * Verify that an expected DomainNameElement will be returned when parsing a buffer that
+ * contained a Domain Name ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseDomainNameElement() throws Exception {
+ String[] testNames = new String[] {"test.com", "abc.com"};
+ DomainNameElement expected = new DomainNameElement(Arrays.asList(testNames));
+
+ ByteBuffer buffer = ByteBuffer.wrap(getDomainNamePayload(testNames));
+ assertEquals(expected,
+ ANQPParser.parseElement(Constants.ANQPElementType.ANQPDomName, buffer));
+ }
+
+ /**
+ * Verify that an expected RoamingConsortiumElement will be returned when parsing a buffer that
+ * contained a Roaming Consortium ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseRoamingConsortium() throws Exception {
+ Long[] ois = new Long[] {0x12345678L, 0x5678L};
+ int[] oisLength = new int[] {4, 2};
+ RoamingConsortiumElement expected = new RoamingConsortiumElement(Arrays.asList(ois));
+
+ ByteBuffer buffer = ByteBuffer.wrap(getRoamingConsortiumPayload(ois, oisLength));
+ assertEquals(expected,
+ ANQPParser.parseElement(Constants.ANQPElementType.ANQPRoamingConsortium, buffer));
+ }
+
+ /**
+ * Verify that an expected NAIRealmElement will be returned when parsing a buffer that
+ * contained a NAI Realm ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseNAIRealmElement() throws Exception {
+ byte[][] testBytes = new byte[][] {NAIRealmDataTestUtil.TEST_REAML_WITH_UTF8_DATA_BYTES};
+ NAIRealmData[] realmDataList = new NAIRealmData[] {NAIRealmDataTestUtil.TEST_REALM_DATA};
+ NAIRealmElement expected = new NAIRealmElement(Arrays.asList(realmDataList));
+
+ ByteBuffer buffer = ByteBuffer.wrap(getNAIRealmPayload(testBytes));
+ assertEquals(expected,
+ ANQPParser.parseElement(Constants.ANQPElementType.ANQPNAIRealm, buffer));
+ }
+
+ /**
+ * Verify that an expected ThreeGPPNetworkElement will be returned when parsing a buffer that
+ * contained a 3GPP Network ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseThreeGPPNetworkElement() throws Exception {
+ byte[][] plmnBytes = new byte[][] {new byte[] {(byte) 0x87, 0x29, 0x10}};
+ String[] plmnList = new String[] {"789012"};
+
+ List<CellularNetwork> networkList = new ArrayList<>();
+ networkList.add(new CellularNetwork(Arrays.asList(plmnList)));
+ ThreeGPPNetworkElement expected = new ThreeGPPNetworkElement(networkList);
+
+ ByteBuffer buffer = ByteBuffer.wrap(getThreeGPPNetworkPayload(
+ new byte[][] {CellularNetworkTestUtil.formatPLMNListIEI(plmnBytes)}));
+ assertEquals(expected,
+ ANQPParser.parseElement(Constants.ANQPElementType.ANQP3GPPNetwork, buffer));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing a buffer that contained a
+ * vendor specific element that contained a non-Hotspot 2.0 ANQP-element.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseNonHS20VendorSpecificElement() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(
+ getVendorSpecificPayload(0x123456, 0x12, 1, new byte[0]));
+ ANQPParser.parseElement(Constants.ANQPElementType.ANQPVendorSpec, buffer);
+ }
+
+ /**
+ * Verify that an expected HSFriendlyNameElement will be returned when parsing a buffer that
+ * contained a vendor specific element that contained a Hotspot 2.0 friendly name
+ * ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseVendorSpecificElementWithHSFriendlyName() throws Exception {
+ String[] language = new String[] {"en"};
+ String[] text = new String[] {"test"};
+
+ // Setup expectation.
+ List<I18Name> nameList = new ArrayList<>();
+ nameList.add(new I18Name(language[0], Locale.forLanguageTag(language[0]), text[0]));
+ HSFriendlyNameElement expected = new HSFriendlyNameElement(nameList);
+
+ byte[] hsFriendlyNameBytes = getHSFriendlyNamePayload(language, text);
+ byte[] data = getVendorSpecificPayload(
+ ANQPParser.VENDOR_SPECIFIC_HS20_OI, ANQPParser.VENDOR_SPECIFIC_HS20_TYPE,
+ Constants.HS_FRIENDLY_NAME, hsFriendlyNameBytes);
+ assertEquals(expected, ANQPParser.parseElement(
+ Constants.ANQPElementType.ANQPVendorSpec, ByteBuffer.wrap(data)));
+ }
+
+ /**
+ * Verify that an expected HSFriendlyNameElement will be returned when parsing a buffer that
+ * contained a Hotspot 2.0 Friendly Name ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseHSFrendlyNameElement() throws Exception {
+ // Test data.
+ String[] language = new String[] {"en"};
+ String[] text = new String[] {"test"};
+
+ // Setup expectation.
+ List<I18Name> nameList = new ArrayList<>();
+ nameList.add(new I18Name(language[0], Locale.forLanguageTag(language[0]), text[0]));
+ HSFriendlyNameElement expected = new HSFriendlyNameElement(nameList);
+
+ ByteBuffer buffer = ByteBuffer.wrap(getHSFriendlyNamePayload(language, text));
+ assertEquals(expected,
+ ANQPParser.parseHS20Element(Constants.ANQPElementType.HSFriendlyName, buffer));
+ }
+
+ /**
+ * Verify that an expected HSWanMetricsElement will be returned when parsing a buffer that
+ * contained a Hotspot 2.0 WAN Metrics ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseHSWANMetricsElement() throws Exception {
+ int status = HSWanMetricsElement.LINK_STATUS_UP;
+ boolean symmetric = false;
+ boolean capped = true;
+ long downlinkSpeed = 0x12453L;
+ long uplinkSpeed = 0x12423L;
+ int downlinkLoad = 0x12;
+ int uplinkLoad = 0x23;
+ int lmd = 0x2321;
+
+ HSWanMetricsElement expected = new HSWanMetricsElement(status, symmetric, capped,
+ downlinkSpeed, uplinkSpeed, downlinkLoad, uplinkLoad, lmd);
+
+ byte[] data = getHSWanMetricsPayload(status, symmetric, capped, downlinkSpeed,
+ uplinkSpeed, downlinkLoad, uplinkLoad, lmd);
+ ByteBuffer buffer = ByteBuffer.wrap(data);
+ assertEquals(expected,
+ ANQPParser.parseHS20Element(Constants.ANQPElementType.HSWANMetrics, buffer));
+ }
+
+ /**
+ * Verify that an expected HSConnectionCapabilityElement will be returned when parsing a
+ * buffer that contained a Hotspot 2.0 Connection Capability ANQP element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseHSConnectionCapabilityElement() throws Exception {
+ int protocol = 12;
+ int port = 23;
+ int status = ProtocolPortTuple.PROTO_STATUS_OPEN;
+
+ List<ProtocolPortTuple> statusList = new ArrayList<>();
+ statusList.add(new ProtocolPortTuple(protocol, port, status));
+ HSConnectionCapabilityElement expected = new HSConnectionCapabilityElement(statusList);
+
+ ByteBuffer buffer = ByteBuffer.wrap(
+ getHSConnectionCapabilityPayload(protocol, port, status));
+ assertEquals(expected,
+ ANQPParser.parseHS20Element(Constants.ANQPElementType.HSConnCapability, buffer));
+ }
+
+ /**
+ * Verify that an expected RawByteElement will be returned when parsing a buffer that
+ * contained a Hotspot 2.0 OSU Providers element.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseHSOUSProvidersElement() throws Exception {
+ byte[] data = new byte[10];
+
+ RawByteElement expected =
+ new RawByteElement(Constants.ANQPElementType.HSOSUProviders, data);
+
+ ByteBuffer buffer = ByteBuffer.wrap(data);
+ assertEquals(expected,
+ ANQPParser.parseHS20Element(Constants.ANQPElementType.HSOSUProviders, buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTest.java
new file mode 100644
index 0000000..50ab189
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.CellularNetwork}.
+ */
+@SmallTest
+public class CellularNetworkTest {
+ private static final byte[] TEST_PLMN_BYTES_1 = new byte[] {0x12, 0x34, 0x56};
+ private static final String TEST_PLMN_STRING_1 = "214653";
+ private static final byte[] TEST_PLMN_BYTES_2 = new byte[] {0x13, (byte) 0xF9, 0x32};
+ private static final String TEST_PLMN_STRING_2 = "31923";
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseBufferWithEmptyBuffer() throws Exception {
+ CellularNetwork.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that a null will be returned when parsing a buffer contained an unsupported IEI type.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithInvalidIEIType() throws Exception {
+ byte[][] plmnsData = new byte[][] {TEST_PLMN_BYTES_1, TEST_PLMN_BYTES_2};
+ byte[] testData = CellularNetworkTestUtil.formatPLMNListIEI(1, plmnsData);
+ assertNull(CellularNetwork.parse(ByteBuffer.wrap(testData)));
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseBufferWithIncompleteData() throws Exception {
+ byte[][] plmnsData = new byte[][] {TEST_PLMN_BYTES_1, TEST_PLMN_BYTES_2};
+ byte[] testData = CellularNetworkTestUtil.formatPLMNListIEI(plmnsData);
+ CellularNetwork.parse(ByteBuffer.wrap(testData, 0, testData.length - 1));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when IEI size and the PLMN count doesn't
+ * match.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithMismatchIEISizeAndPLMNCount() throws Exception {
+ byte[][] plmnsData = new byte[][] {TEST_PLMN_BYTES_1, TEST_PLMN_BYTES_2};
+ // Get test data with IEI size set to incorrect value.
+ byte[] testData = CellularNetworkTestUtil.formatPLMNListIEI(
+ CellularNetwork.IEI_TYPE_PLMN_LIST, plmnsData, true);
+ CellularNetwork.parse(ByteBuffer.wrap(testData));
+ }
+
+ /**
+ * Verify that the expected ProtocolPortTuple is returned when parsing a buffer contained
+ * the test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData() throws Exception {
+ byte[][] plmnsData = new byte[][] {TEST_PLMN_BYTES_1, TEST_PLMN_BYTES_2};
+ byte[] testData = CellularNetworkTestUtil.formatPLMNListIEI(plmnsData);
+
+ // Setup the expected CellularNetwork.
+ List<String> plmnList = new ArrayList<>();
+ plmnList.add(TEST_PLMN_STRING_1);
+ plmnList.add(TEST_PLMN_STRING_2);
+ CellularNetwork expected = new CellularNetwork(plmnList);
+
+ assertEquals(expected, CellularNetwork.parse(ByteBuffer.wrap(testData)));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTestUtil.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTestUtil.java
new file mode 100644
index 0000000..dad2919
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/CellularNetworkTestUtil.java
@@ -0,0 +1,94 @@
+/*
+ * 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.hotspot2.anqp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Utility class for formatting IEI (Information Element Identity) data for testing.
+ */
+public class CellularNetworkTestUtil {
+ /**
+ * Format and return PLMN List IEI with the given PLMN list data.
+ *
+ * @param plmnList The array of PLMN data
+ * @return byte[]
+ * @throws IOException
+ */
+ public static byte[] formatPLMNListIEI(byte[][] plmnList) throws IOException {
+ return formatPLMNListIEI(CellularNetwork.IEI_TYPE_PLMN_LIST, plmnList);
+ }
+
+ /**
+ * Format and return PLMN List IEI with the given IEI type and PLMN list data. This
+ * allows the test to use an invalid IEI type for testing purpose.
+ *
+ * @param ieiType The IEI type
+ * @param plmnList The array of PLMN data
+ * @return byte[]
+ * @throws IOException
+ */
+ public static byte[] formatPLMNListIEI(int ieiType, byte[][] plmnList) throws IOException {
+ return formatPLMNListIEI(ieiType, plmnList, false);
+ }
+
+ /**
+ * Format and return PLMN List IEI with the given IEI type and PLMN list data. This also
+ * allows the test to intentionally setting an incorrect size value.
+ *
+ * @param ieiType The IEI type
+ * @param plmnList The array of PLMN data
+ * @param setWrongSize Flag for setting incorrect IEI size
+ * @return byte[]
+ * @throws IOException
+ */
+ public static byte[] formatPLMNListIEI(int ieiType, byte[][] plmnList, boolean setWrongSize)
+ throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+
+ // Calculate the total bytes for all the PLMNs.
+ int plmnsSize = getDataSize(plmnList);
+
+ // Use incorrect size intentionally.
+ if (setWrongSize) plmnsSize -= 1;
+
+ stream.write((byte) ieiType);
+ // One extra byte for the PLMN count field.
+ stream.write((byte) ((plmnsSize + 1) & CellularNetwork.IEI_CONTENT_LENGTH_MASK));
+ stream.write((byte) plmnList.length);
+ for (byte[] plmn : plmnList) {
+ stream.write(plmn);
+ }
+
+ return stream.toByteArray();
+ }
+
+ /**
+ * Return the number of bytes in a 2D array.
+ *
+ * @param dataArray The 2D array
+ * @return The number of bytes in the 2D array
+ */
+ public static int getDataSize(byte[][] dataArray) {
+ int size = 0;
+ for (byte[] data : dataArray) {
+ size += data.length;
+ }
+ return size;
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java
new file mode 100644
index 0000000..d17a7fa
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/DomainNameElementTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.DomainNameElement}.
+ */
+@SmallTest
+public class DomainNameElementTest {
+ private static final String TEST_DOMAIN_NAME1 = "test1.com";
+ private static final String TEST_DOMAIN_NAME2 = "test2.com";
+
+ /**
+ * Helper function for appending a Domain Name to an output stream.
+ *
+ * @param stream Stream to write to
+ * @param domain The domain name string
+ * @throws IOException
+ */
+ private void appendDomain(ByteArrayOutputStream stream, String domain) throws IOException {
+ byte[] domainBytes = domain.getBytes(StandardCharsets.ISO_8859_1);
+ stream.write((byte) domainBytes.length);
+ stream.write(domainBytes);
+ }
+
+ /**
+ * Helper function for generating test data.
+ *
+ * @return byte[] of data
+ * @throws IOException
+ */
+ private byte[] getTestData(String[] domains) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ for (String domain : domains) {
+ appendDomain(stream, domain);
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Verify that a DomainNameElement with empty domain list will be returned when parsing an
+ * empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseEmptyBuffer() throws Exception {
+ assertTrue(DomainNameElement.parse(ByteBuffer.allocate(0)).getDomains().isEmpty());
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[] {TEST_DOMAIN_NAME1}));
+ buffer.limit(buffer.remaining() - 1);
+ DomainNameElement.parse(buffer);
+ }
+
+ /**
+ * Verify that a DomainNameElement with expected domain list will be returned when parsing a
+ * buffer contained valid domain name list.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithValidDomainNames() throws Exception {
+ byte[] testData = getTestData(new String[] {TEST_DOMAIN_NAME1, TEST_DOMAIN_NAME2});
+ ByteBuffer buffer = ByteBuffer.wrap(testData);
+
+ // Setup expected element.
+ List<String> domainList = new ArrayList<>();
+ domainList.add(TEST_DOMAIN_NAME1);
+ domainList.add(TEST_DOMAIN_NAME2);
+ DomainNameElement expectedElement = new DomainNameElement(domainList);
+
+ assertEquals(expectedElement, DomainNameElement.parse(buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java
new file mode 100644
index 0000000..aef2b86
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSConnectionCapabilityElementTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSConnectionCapabilityElement}.
+ */
+@SmallTest
+public class HSConnectionCapabilityElementTest {
+ private static final ProtocolPortTuple TEST_TUPLE1 =
+ new ProtocolPortTuple(1, 2, ProtocolPortTuple.PROTO_STATUS_CLOSED);
+ private static final ProtocolPortTuple TEST_TUPLE2 =
+ new ProtocolPortTuple(3, 4, ProtocolPortTuple.PROTO_STATUS_OPEN);
+
+ /**
+ * Helper function for writing a ProtocolPortTuple into a buffer.
+ *
+ * @param buffer The buffer to write to
+ * @param tuple The tuple to write
+ */
+ private void appendProtocolPortTuple(ByteBuffer buffer, ProtocolPortTuple tuple) {
+ buffer.put((byte) tuple.getProtocol());
+ buffer.putShort((short) tuple.getPort());
+ buffer.put((byte) tuple.getStatus());
+ }
+
+ /**
+ * Helper function for generating a buffer with test data.
+ *
+ * @param tuples Tuples to put in the buffer
+ * @return {@link ByteBuffer}
+ */
+ private ByteBuffer getTestBuffer(ProtocolPortTuple[] tuples) {
+ ByteBuffer buffer = ByteBuffer.allocate(tuples.length * ProtocolPortTuple.RAW_BYTE_SIZE)
+ .order(ByteOrder.LITTLE_ENDIAN);
+ for (ProtocolPortTuple tuple : tuples) {
+ appendProtocolPortTuple(buffer, tuple);
+ }
+ buffer.position(0);
+ return buffer;
+ }
+
+ /**
+ * Verify that a HSConnectionCapabilityElement with an empty status list will be returned
+ * when parsing an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseEmptyBuffer() throws Exception {
+ HSConnectionCapabilityElement element =
+ HSConnectionCapabilityElement.parse(ByteBuffer.allocate(0));
+ assertTrue(element.getStatusList().isEmpty());
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a buffer without
+ * the complete tuple data (missing status field).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseBufferWithLessThanMinimumSize() throws Exception {
+ ByteBuffer buffer = ByteBuffer.allocate(ProtocolPortTuple.RAW_BYTE_SIZE - 1);
+ buffer.put(new byte[ProtocolPortTuple.RAW_BYTE_SIZE - 1]);
+ buffer.position(0);
+ HSConnectionCapabilityElement.parse(buffer);
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a buffer that contained
+ * incomplete bytes for a tuple.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseBufferWithIncompleteTupleBytes() throws Exception {
+ // Construct a buffer which will contained a tuple and an extra byte at the end.
+ ByteBuffer buffer = ByteBuffer.allocate(ProtocolPortTuple.RAW_BYTE_SIZE + 1);
+ appendProtocolPortTuple(buffer, TEST_TUPLE1);
+ buffer.put((byte) 0);
+ buffer.position(0);
+ HSConnectionCapabilityElement.parse(buffer);
+ }
+
+ /**
+ * Verify that the expected HSConnectionCapabilityElement is returned when parsing
+ * a buffer containing the test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData() throws Exception {
+ ByteBuffer buffer = getTestBuffer(new ProtocolPortTuple[] {TEST_TUPLE1, TEST_TUPLE2});
+
+ // Setup expected element.
+ List<ProtocolPortTuple> tupleList = new ArrayList<>();
+ tupleList.add(TEST_TUPLE1);
+ tupleList.add(TEST_TUPLE2);
+ HSConnectionCapabilityElement expected = new HSConnectionCapabilityElement(tupleList);
+
+ assertEquals(expected, HSConnectionCapabilityElement.parse(buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java
new file mode 100644
index 0000000..e228e03
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSFriendlyNameElementTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSFriendlyNameElement}.
+ */
+@SmallTest
+public class HSFriendlyNameElementTest {
+ private static final String TEST_LANGUAGE = "en";
+ private static final Locale TEST_LOCALE = Locale.forLanguageTag(TEST_LANGUAGE);
+ private static final String TEST_OPERATOR_NAME1 = "Operator1";
+ private static final String TEST_OPERATOR_NAME2 = "Operator2";
+
+ /**
+ * Helper function for appending a Operator Name to an output stream.
+ *
+ * @param stream Stream to write to
+ * @param operator The name of the operator
+ * @throws IOException
+ */
+ private void appendOperatorName(ByteArrayOutputStream stream, String operator)
+ throws IOException {
+ byte[] nameBytes = operator.getBytes(StandardCharsets.UTF_8);
+ int length = I18Name.LANGUAGE_CODE_LENGTH + operator.length();
+ stream.write((byte) length);
+ stream.write(TEST_LANGUAGE.getBytes(StandardCharsets.US_ASCII));
+ stream.write(new byte[]{(byte) 0x0}); // Padding for language code.
+ stream.write(nameBytes);
+ }
+
+ /**
+ * Helper function for generating test data.
+ *
+ * @return byte[] of data
+ * @throws IOException
+ */
+ private byte[] getTestData(String[] names) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ for (String name : names) {
+ appendOperatorName(stream, name);
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Verify that HSFriendlyNameElement with a empty operator name list will be returned when
+ * parsing an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithEmptyBuffer() throws Exception {
+ assertTrue(HSFriendlyNameElement.parse(ByteBuffer.allocate(0)).getNames().isEmpty());
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseBufferWithTruncatedByte() throws Exception {
+ byte[] testData = getTestData(new String[] {TEST_OPERATOR_NAME1});
+ // Truncate a byte at the end.
+ ByteBuffer buffer = ByteBuffer.allocate(testData.length - 1);
+ buffer.put(testData, 0, testData.length - 1);
+ buffer.position(0);
+ HSFriendlyNameElement.parse(buffer);
+ }
+
+ /**
+ * Verify that an expected HSFriendlyNameElement will be returned when parsing a buffer
+ * containing the default test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithDefaultTestData() throws Exception {
+ byte[] testData = getTestData(new String[] {TEST_OPERATOR_NAME1, TEST_OPERATOR_NAME2});
+ ByteBuffer buffer = ByteBuffer.allocate(testData.length);
+ buffer.put(testData);
+ buffer.position(0);
+
+ // Setup expected element.
+ List<I18Name> nameList = new ArrayList<>();
+ nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_OPERATOR_NAME1));
+ nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_OPERATOR_NAME2));
+ HSFriendlyNameElement expectedElement = new HSFriendlyNameElement(nameList);
+
+ assertEquals(expectedElement, HSFriendlyNameElement.parse(buffer));
+ }
+
+ /**
+ * Verify that an expected HSFriendlyNameElement will be returned when parsing a buffer
+ * containing a operator name with the maximum length.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithMaxLengthOperatoreName() throws Exception {
+ // Operator name with the maximum length.
+ byte[] textData = new byte[HSFriendlyNameElement.MAXIMUM_OPERATOR_NAME_LENGTH];
+ Arrays.fill(textData, (byte) 'a');
+ String text = new String(textData);
+ byte[] testData = getTestData(new String[] {text});
+ ByteBuffer buffer = ByteBuffer.allocate(testData.length);
+ buffer.put(testData);
+ buffer.position(0);
+
+ // Setup expected element.
+ List<I18Name> nameList = new ArrayList<>();
+ nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, text));
+ HSFriendlyNameElement expectedElement = new HSFriendlyNameElement(nameList);
+
+ assertEquals(expectedElement, HSFriendlyNameElement.parse(buffer));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing a buffer containing a
+ * operator name that exceeds the maximum length.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithOperatorNameLengthExceedMax() throws Exception {
+ byte[] textData = new byte[HSFriendlyNameElement.MAXIMUM_OPERATOR_NAME_LENGTH + 1];
+ Arrays.fill(textData, (byte) 'a');
+ String text = new String(textData);
+ byte[] testData = getTestData(new String[] {text});
+ ByteBuffer buffer = ByteBuffer.allocate(testData.length);
+ buffer.put(testData);
+ buffer.position(0);
+ HSFriendlyNameElement.parse(buffer);
+ }
+
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java
new file mode 100644
index 0000000..8c53fe3
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/HSWanMetricsElementTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.HSWanMetricsElement}.
+ */
+@SmallTest
+public class HSWanMetricsElementTest {
+ private static final int TEST_LINK_STATUS = HSWanMetricsElement.LINK_STATUS_UP;
+ private static final boolean TEST_SYMMETRIC_LINK = true;
+ private static final boolean TEST_AT_CAPACITY = true;
+ private static final long TEST_DOWNLINK_SPEED = 0x1234556L;
+ private static final long TEST_UPLINK_SPEED = 0x342343L;
+ private static final int TEST_DOWNLINK_LOAD = 0x23;
+ private static final int TEST_UPLINK_LOAD = 0x45;
+ private static final int TEST_LMD = 0x2132;
+
+ /**
+ * Helper function for generating a ByteBuffer with the test data.
+ *
+ * @return {@link ByteBuffer}
+ */
+ private ByteBuffer getTestBuffer() {
+ ByteBuffer buffer = ByteBuffer.allocate(HSWanMetricsElement.EXPECTED_BUFFER_SIZE)
+ .order(ByteOrder.LITTLE_ENDIAN);
+ int wanInfo = TEST_LINK_STATUS & HSWanMetricsElement.LINK_STATUS_MASK;
+ if (TEST_SYMMETRIC_LINK) wanInfo |= HSWanMetricsElement.SYMMETRIC_LINK_MASK;
+ if (TEST_AT_CAPACITY) wanInfo |= HSWanMetricsElement.AT_CAPACITY_MASK;
+ buffer.put((byte) wanInfo);
+ buffer.putInt((int) (TEST_DOWNLINK_SPEED & 0xFFFFFFFFL));
+ buffer.putInt((int) (TEST_UPLINK_SPEED & 0xFFFFFFFFL));
+ buffer.put((byte) (TEST_DOWNLINK_LOAD & 0xFF));
+ buffer.put((byte) (TEST_UPLINK_LOAD & 0xFF));
+ buffer.putShort((short) (TEST_LMD & 0xFFFF));
+ buffer.position(0);
+ return buffer;
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseEmptyBuffer() throws Exception {
+ HSWanMetricsElement.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when a buffer with size less than the
+ * expected.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithLessThanExpectedSize() throws Exception {
+ ByteBuffer buffer = ByteBuffer.allocate(HSWanMetricsElement.EXPECTED_BUFFER_SIZE - 1);
+ buffer.put(new byte[HSWanMetricsElement.EXPECTED_BUFFER_SIZE - 1]);
+ buffer.position(0);
+ HSWanMetricsElement.parse(buffer);
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when a buffer with size more than the
+ * expected.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithMoreThanExpectedSize() throws Exception {
+ ByteBuffer buffer = ByteBuffer.allocate(HSWanMetricsElement.EXPECTED_BUFFER_SIZE + 1);
+ buffer.put(new byte[HSWanMetricsElement.EXPECTED_BUFFER_SIZE + 1]);
+ buffer.position(0);
+ HSWanMetricsElement.parse(buffer);
+ }
+
+ /**
+ * Verify that the expected HSWanMetricsElement is returned when parsing
+ * a buffer containing the test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData() throws Exception {
+ ByteBuffer buffer = getTestBuffer();
+ HSWanMetricsElement expectedElement = new HSWanMetricsElement(
+ TEST_LINK_STATUS, TEST_SYMMETRIC_LINK, TEST_AT_CAPACITY,
+ TEST_DOWNLINK_SPEED, TEST_UPLINK_SPEED, TEST_DOWNLINK_LOAD,
+ TEST_UPLINK_LOAD, TEST_LMD);
+ assertEquals(expectedElement, HSWanMetricsElement.parse(buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java
new file mode 100644
index 0000000..6920e58
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/I18NameTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.I18Name}.
+ */
+@SmallTest
+public class I18NameTest {
+ private static final String TEST_LANGUAGE = "en";
+ private static final Locale TEST_LOCALE = Locale.forLanguageTag(TEST_LANGUAGE);
+ private static final String TEST_TEXT = "Hello World";
+
+ /**
+ * Helper function for returning byte array containing test data.
+ *
+ * @param language The language code string
+ * @param text The text string
+ * @return byte[]
+ * @throws IOException
+ */
+ private byte[] getTestData(String language, String text) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ stream.write(language.getBytes(StandardCharsets.US_ASCII));
+ stream.write(new byte[]{(byte) 0x0}); // Padding for language code.
+ stream.write(text.getBytes(StandardCharsets.UTF_8));
+ return stream.toByteArray();
+ }
+
+ /**
+ * Helper function for generating default test data. The test data include the language code
+ * and text field.
+ *
+ * @return byte[] of data
+ * @throws IOException
+ */
+ private byte[] getDefaultTestData() throws IOException {
+ return getTestData(TEST_LANGUAGE, TEST_TEXT);
+ }
+
+ /**
+ * Helper function for returning a buffer containing a I18Name test data.
+ *
+ * @Param data The byte array of I18Name data
+ * @param length The length value to set in the I18Name header
+ * @return {@link ByteBuffer}
+ * @throws IOException
+ */
+ private ByteBuffer getTestBuffer(byte[] data, int length) throws IOException {
+ // Allocate extra byte for storing the length field.
+ ByteBuffer buffer = ByteBuffer.allocate(data.length + 1);
+ buffer.put((byte) length);
+ buffer.put(data);
+ buffer.position(0);
+ return buffer;
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ I18Name.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when the length field is set to more
+ * than the actual buffer size.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ byte[] data = getDefaultTestData();
+ ByteBuffer buffer = getTestBuffer(data, data.length);
+ buffer.limit(buffer.remaining() - 1);
+ I18Name.parse(buffer);
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when the length field is set to less than
+ * the minimum.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithLengthLessThanMinimum() throws Exception {
+ byte[] data = getDefaultTestData();
+ I18Name.parse(getTestBuffer(data, I18Name.MINIMUM_LENGTH - 1));
+ }
+
+ /**
+ * Verify that the expected I18Name will be returned when parsing a buffer contained the
+ * predefined test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithDefaultTestData() throws Exception {
+ byte[] data = getDefaultTestData();
+ I18Name actualName = I18Name.parse(getTestBuffer(data, data.length));
+ I18Name expectedName = new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_TEXT);
+ assertEquals(expectedName, actualName);
+ }
+
+ /**
+ * Verify that the expected I18Name will be returned when parsing a buffer contained
+ * a non-English (French) language.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithFrenchData() throws Exception {
+ // Test data for French.
+ String language = "fr";
+ String text = "Hello World";
+ byte[] data = getTestData(language, text);
+ I18Name actualName = I18Name.parse(getTestBuffer(data, data.length));
+ I18Name expectedName = new I18Name(language, Locale.forLanguageTag(language), text);
+ assertEquals(expectedName, actualName);
+ }
+
+ /**
+ * Verify that an I18Name with an empty text will be returned when parsing a buffer contained
+ * an empty text field.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithEmptyText() throws Exception {
+ byte[] data = getTestData(TEST_LANGUAGE, "");
+ I18Name actualName = I18Name.parse(getTestBuffer(data, data.length));
+ I18Name expectedName = new I18Name(TEST_LANGUAGE, TEST_LOCALE, "");
+ assertEquals(expectedName, actualName);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java
new file mode 100644
index 0000000..bbe8148
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/IPAddressTypeAvailabilityElementTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.IPAddressTypeAvailabilityElement}.
+ */
+@SmallTest
+public class IPAddressTypeAvailabilityElementTest {
+ private static final int TEST_IPV4_AVAILABILITY =
+ IPAddressTypeAvailabilityElement.IPV4_PUBLIC;
+ private static final int TEST_IPV6_AVAILABILITY =
+ IPAddressTypeAvailabilityElement.IPV6_AVAILABLE;
+
+ private static int getIPAvailability() {
+ return (TEST_IPV4_AVAILABILITY << 2) | TEST_IPV6_AVAILABILITY;
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferEmptyBuffer() throws Exception {
+ IPAddressTypeAvailabilityElement.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing an buffer containing excess
+ * data.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithExcessData() throws Exception {
+ ByteBuffer buffer = ByteBuffer.allocate(
+ IPAddressTypeAvailabilityElement.EXPECTED_BUFFER_LENGTH + 1);
+ buffer.put((byte) getIPAvailability());
+ buffer.put((byte) 0); // Excess data.
+ buffer.position(0);
+ IPAddressTypeAvailabilityElement.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that the expected IPAddressTypeAvailabilityElement is returned when parsing
+ * a buffer containing the test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData() throws Exception {
+ ByteBuffer buffer = ByteBuffer.allocate(
+ IPAddressTypeAvailabilityElement.EXPECTED_BUFFER_LENGTH);
+ buffer.put((byte) getIPAvailability());
+ buffer.position(0);
+
+ IPAddressTypeAvailabilityElement expectedElement = new IPAddressTypeAvailabilityElement(
+ TEST_IPV4_AVAILABILITY, TEST_IPV6_AVAILABILITY);
+ assertEquals(expectedElement, IPAddressTypeAvailabilityElement.parse(buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTest.java
new file mode 100644
index 0000000..e600e3b
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTest.java
@@ -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.
+ */
+
+package com.android.server.wifi.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.NAIRealmData}.
+ */
+@SmallTest
+public class NAIRealmDataTest {
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ NAIRealmData.parse(ByteBuffer.wrap(new byte[0]));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(NAIRealmDataTestUtil.TEST_REAML_WITH_UTF8_DATA_BYTES);
+ buffer.limit(buffer.remaining() - 1);
+ NAIRealmData.parse(buffer);
+ }
+
+ /**
+ * Verify that an expected NAIRealmData will be returned when parsing a buffer contained
+ * the test data with realm string encoded using UTF8.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithUTF8EncodedNAIRealmData() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(NAIRealmDataTestUtil.TEST_REAML_WITH_UTF8_DATA_BYTES);
+ assertEquals(NAIRealmDataTestUtil.TEST_REALM_DATA, NAIRealmData.parse(buffer));
+ }
+
+ /**
+ * Verify that the expected NAIRealmData will be returned when parsing a buffer contained
+ * the test data with realm string encoded using non-UTF8.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithNonUTF8EncodedNAIRealmData() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(
+ NAIRealmDataTestUtil.TEST_REAML_WITH_NON_UTF8_DATA_BYTES);
+ assertEquals(NAIRealmDataTestUtil.TEST_REALM_DATA, NAIRealmData.parse(buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTestUtil.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTestUtil.java
new file mode 100644
index 0000000..1ce3044
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmDataTestUtil.java
@@ -0,0 +1,119 @@
+/*
+ * 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.hotspot2.anqp;
+
+import android.net.wifi.EAPConstants;
+
+import com.android.server.wifi.hotspot2.anqp.eap.AuthParam;
+import com.android.server.wifi.hotspot2.anqp.eap.CredentialType;
+import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility class containing test data for NAI Realm Data.
+ */
+public class NAIRealmDataTestUtil {
+ /**
+ * Raw bytes for EAP Method.
+ */
+ private static final byte[] TEST_EAP_METHOD_BYTES =
+ new byte[] {0x05 /* length */, 0x0D /* EAP_TLS */, 0x01 /* Auth Param Count */,
+ 0x05 /* CredentialType */, 0x01, 0x02 /* USIM */};
+
+ /**
+ * NAI Realm strings.
+ */
+ private static final String[] TEST_REALMS = new String[] {"test1", "test2"};
+
+ /**
+ * Setup expected EAPMethod list.
+ */
+ private static final Map<Integer, Set<AuthParam>> TEST_EAP_METHOD_AUTH_PARAMS =
+ new HashMap<>();
+ private static final Set<AuthParam> TEST_EAP_METHOD_CREDENTIAL_TYPE_PARAMS = new HashSet<>();
+ private static final List<EAPMethod> TEST_EAP_METHOD_LIST = new ArrayList<>();
+ static {
+ TEST_EAP_METHOD_CREDENTIAL_TYPE_PARAMS.add(new CredentialType(
+ AuthParam.PARAM_TYPE_CREDENTIAL_TYPE, CredentialType.CREDENTIAL_TYPE_USIM));
+ TEST_EAP_METHOD_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_CREDENTIAL_TYPE,
+ TEST_EAP_METHOD_CREDENTIAL_TYPE_PARAMS);
+
+ TEST_EAP_METHOD_LIST.add(new EAPMethod(EAPConstants.EAP_TLS, TEST_EAP_METHOD_AUTH_PARAMS));
+ }
+
+ /**
+ * Setup expected NAIRealmData.
+ */
+ public static final NAIRealmData TEST_REALM_DATA =
+ new NAIRealmData(Arrays.asList(TEST_REALMS), TEST_EAP_METHOD_LIST);
+
+ public static byte[] TEST_REAML_WITH_UTF8_DATA_BYTES = formatNAIRealmData(true);
+ public static byte[] TEST_REAML_WITH_NON_UTF8_DATA_BYTES = formatNAIRealmData(false);
+
+ /**
+ * Helper function for returning raw bytes of NAI Realm Data (including the length field) for
+ * testing.
+ *
+ * @param utfEncoding Flag indicating the UTF encoding of the realm string
+ * @return byte[]
+ */
+ private static byte[] formatNAIRealmData(boolean utfEncoding) {
+ try {
+ byte[] realmData = getNAIRealmData(utfEncoding);
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ // Realm Data length in Little-Endian.
+ stream.write((byte) realmData.length);
+ stream.write((byte) realmData.length >> 8);
+ stream.write(realmData);
+ return stream.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Helper function for returning raw bytes of NAI Realm Data payload.
+ *
+ * @param utfEncoding Flag indicating the encoding of NAI Realm string
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getNAIRealmData(boolean utfEncoding) throws IOException {
+ String realmsStr = String.join(NAIRealmData.NAI_REALM_STRING_SEPARATOR, TEST_REALMS);
+ byte[] realmStrData = realmsStr.getBytes(
+ utfEncoding ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII);
+
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ // NAI Realm Encoding byte.
+ stream.write((byte) (utfEncoding ? NAIRealmData.NAI_ENCODING_UTF8_MASK : 0));
+ stream.write((byte) realmStrData.length);
+ stream.write(realmStrData);
+ stream.write((byte) 1); // EAP Method count
+ stream.write(TEST_EAP_METHOD_BYTES);
+ return stream.toByteArray();
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmElementTest.java
new file mode 100644
index 0000000..6b7aa84
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/NAIRealmElementTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.NAIRealmElement}.
+ */
+@SmallTest
+public class NAIRealmElementTest {
+ /**
+ * Helper function for returning a ByteBuffer containing raw bytes for NAI Realm Element
+ * with specified number of NAI Realm Data.
+ *
+ * @param dataCount The number of NAI Realm Data to be added to the buffer
+ * @return {@link ByteBuffer}
+ */
+ private static ByteBuffer getTestBufferWithNAIRealmData(int dataCount) {
+ int dataLength = NAIRealmDataTestUtil.TEST_REAML_WITH_UTF8_DATA_BYTES.length * dataCount;
+ // 2-bytes for the NAI Realm Data count header.
+ ByteBuffer buffer = ByteBuffer.allocate(dataLength + 2).order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putShort((short) dataCount);
+ for (int i = 0; i < dataCount; i++) {
+ buffer.put(NAIRealmDataTestUtil.TEST_REAML_WITH_UTF8_DATA_BYTES);
+ }
+ buffer.position(0);
+ return buffer;
+ }
+
+ /**
+ * Verify that a NAIRealmElement with an empty NAIRealmData list will be returned when parsing
+ * from an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseEmptyBuffer() throws Exception {
+ assertTrue(NAIRealmElement.parse(
+ ByteBuffer.wrap(new byte[0])).getRealmDataList().isEmpty());
+ }
+
+ /**
+ * Verify that an expected NAIRealmElement will be returned when parsing a buffer containing
+ * a NAI Realm Element with single NAI Realm Data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithSingleNAIRealmData() throws Exception {
+ // Setup expected NAIRealmElement.
+ List<NAIRealmData> realmDataList = new ArrayList<>();
+ realmDataList.add(NAIRealmDataTestUtil.TEST_REALM_DATA);
+ NAIRealmElement expected = new NAIRealmElement(realmDataList);
+
+ assertEquals(expected, NAIRealmElement.parse(getTestBufferWithNAIRealmData(1)));
+ }
+
+ /**
+ * Verify that an expected NAIRealmElement will be returned when parsing a buffer containing
+ * a NAI Realm Element with multiple NAI Realm Data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithMultipleNAIRealmData() throws Exception {
+ // Setup expected NAIRealmElement.
+ List<NAIRealmData> realmDataList = new ArrayList<>();
+ realmDataList.add(NAIRealmDataTestUtil.TEST_REALM_DATA);
+ realmDataList.add(NAIRealmDataTestUtil.TEST_REALM_DATA);
+ NAIRealmElement expected = new NAIRealmElement(realmDataList);
+
+ assertEquals(expected, NAIRealmElement.parse(getTestBufferWithNAIRealmData(2)));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java
new file mode 100644
index 0000000..b4bfaf1
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ProtocolPortTupleTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.ProtocolPortTuple}.
+ */
+@SmallTest
+public class ProtocolPortTupleTest {
+ private static final int TEST_PROTOCOL = 1;
+ private static final int TEST_PORT = 2;
+ private static final int TEST_STATUS = ProtocolPortTuple.PROTO_STATUS_CLOSED;
+
+ /**
+ * Helper function for generating a buffer with test data.
+ *
+ * @param protocol Protocol value
+ * @param port Port value
+ * @param status Status value
+ * @return {@link ByteBuffer}
+ */
+ private ByteBuffer getTestBuffer(int protocol, int port, int status) {
+ ByteBuffer buffer = ByteBuffer.allocate(ProtocolPortTuple.RAW_BYTE_SIZE)
+ .order(ByteOrder.LITTLE_ENDIAN);
+ buffer.put((byte) protocol);
+ buffer.putShort((short) port);
+ buffer.put((byte) status);
+ buffer.position(0);
+ return buffer;
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ ProtocolPortTuple.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a buffer without
+ * the complete tuple data (missing status field).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseBufferWithIncompleteData() throws Exception {
+ ByteBuffer buffer = ByteBuffer.allocate(ProtocolPortTuple.RAW_BYTE_SIZE - 1);
+ buffer.put(new byte[ProtocolPortTuple.RAW_BYTE_SIZE - 1]);
+ buffer.position(0);
+ ProtocolPortTuple.parse(buffer);
+ }
+
+ /**
+ * Verify that the expected ProtocolPortTuple is returned when parsing a buffer contained
+ * the test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData() throws Exception {
+ ByteBuffer buffer = getTestBuffer(TEST_PROTOCOL, TEST_PORT, TEST_STATUS);
+ ProtocolPortTuple expected = new ProtocolPortTuple(TEST_PROTOCOL, TEST_PORT, TEST_STATUS);
+ assertEquals(expected, ProtocolPortTuple.parse(buffer));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java
new file mode 100644
index 0000000..e61797a
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RawByteElementTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.RawByteElement}.
+ */
+@SmallTest
+public class RawByteElementTest {
+ private static final Constants.ANQPElementType TEST_ELEMENT_ID =
+ Constants.ANQPElementType.HSOSUProviders;
+
+ /**
+ * Verify that a RawByteElement with an empty payload will be returned when parsing
+ * an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseEmptyBuffer() throws Exception {
+ byte[] data = new byte[0];
+ RawByteElement actual = RawByteElement.parse(TEST_ELEMENT_ID, ByteBuffer.wrap(data));
+ RawByteElement expected = new RawByteElement(TEST_ELEMENT_ID, data);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Verify that the expected RawByteElement will be returned when parsing a non-empty
+ * buffer.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseNonEmptyBuffer() throws Exception {
+ byte[] data = new byte[10];
+ RawByteElement actual = RawByteElement.parse(TEST_ELEMENT_ID, ByteBuffer.wrap(data));
+ RawByteElement expected = new RawByteElement(TEST_ELEMENT_ID, data);
+ assertEquals(expected, actual);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java
new file mode 100644
index 0000000..4e3bd1f
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/RoamingConsortiumElementTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement}.
+ */
+@SmallTest
+public class RoamingConsortiumElementTest {
+ // Default test data. Each test data contained a pair indicating the number of bytes for the
+ // OI and the value of the OI.
+ private static final Pair<Integer, Long> TEST_OI1 = new Pair<Integer, Long>(1, 0x12L);
+ private static final Pair<Integer, Long> TEST_OI2 = new Pair<Integer, Long>(2, 0x1234L);
+ private static final Pair<Integer, Long> TEST_OI3 = new Pair<Integer, Long>(4, 0x12345678L);
+ private static final Pair<Integer, Long> TEST_OI4 = new Pair<Integer, Long>(8, 0x1234567890L);
+
+ /**
+ * Helper function for appending an OI field to the given output stream.
+ *
+ * @param stream The output stream to write to
+ * @param OI The OI to write to the output stream
+ */
+ private void appendOI(ByteArrayOutputStream stream, Pair<Integer, Long> oi) {
+ stream.write(oi.first.byteValue());
+ // Write the OI data in big-endian.
+ for (int i = oi.first.intValue() - 1; i >= 0; i--) {
+ stream.write((byte) ((oi.second.longValue() >> i * Byte.SIZE) & 0xFF));
+ }
+ }
+ /**
+ * Helper function for generating test data with the provided OIs.
+ *
+ * @param OIs The OIs to generate the data with
+ * @return byte[] of data
+ * @throws IOException
+ */
+ private byte[] getTestData(List<Pair<Integer, Long>> ois) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ for (Pair<Integer, Long> oi : ois) {
+ appendOI(stream, oi);
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Helper function for generating test data using the predefined OIs.
+ *
+ * @return byte[] of data
+ * @throws IOException
+ */
+ private byte[] getDefaultTestData() throws IOException {
+ List<Pair<Integer, Long>> oiList = new ArrayList<>();
+ oiList.add(TEST_OI1);
+ oiList.add(TEST_OI2);
+ oiList.add(TEST_OI3);
+ oiList.add(TEST_OI4);
+ return getTestData(oiList);
+ }
+
+ /**
+ * Helper function for creating a RoamingConsortiumElement using the predefined OIs.
+ *
+ * @return {@link RoamingConsortiumElement}
+ */
+ private RoamingConsortiumElement getDefaultElement() {
+ List<Long> oiList = new ArrayList<>();
+ oiList.add(TEST_OI1.second);
+ oiList.add(TEST_OI2.second);
+ oiList.add(TEST_OI3.second);
+ oiList.add(TEST_OI4.second);
+ return new RoamingConsortiumElement(oiList);
+ }
+
+ /**
+ * Verify that no exception will be thrown when parsing an empty buffer and the returned
+ * RoamingConsortiumElement will contained an empty list of OIs.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseEmptyBuffer() throws Exception {
+ RoamingConsortiumElement element = RoamingConsortiumElement.parse(ByteBuffer.allocate(0));
+ assertTrue(element.getOIs().isEmpty());
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(getDefaultTestData());
+ buffer.limit(buffer.remaining() - 1);
+ RoamingConsortiumElement.parse(buffer);
+ }
+
+ /**
+ * Verify that an expected RoamingConsortiumElement will be returned when parsing a buffer
+ * containing valid data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithDefaultTestData() throws Exception {
+ // Setup expected element.
+ RoamingConsortiumElement expectedElement = getDefaultElement();
+
+ ByteBuffer buffer = ByteBuffer.wrap(getDefaultTestData());
+ assertEquals(expectedElement, RoamingConsortiumElement.parse(buffer));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing a buffer contained an OI length
+ * that's less than minimum allowed.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithOILengthLessThanMinimum() throws Exception {
+ ByteBuffer buffer = ByteBuffer.allocate(1);
+ buffer.put((byte) (RoamingConsortiumElement.MINIMUM_OI_LENGTH - 1));
+ buffer.position(0);
+ RoamingConsortiumElement.parse(buffer);
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing a buffer contained an OI length
+ * that's more than maximum allowed.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithOILengthMoreThanMaximum() throws Exception {
+ ByteBuffer buffer = ByteBuffer.allocate(1);
+ buffer.put((byte) (RoamingConsortiumElement.MAXIMUM_OI_LENGTH + 1));
+ buffer.position(0);
+ RoamingConsortiumElement.parse(buffer);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElementTest.java
new file mode 100644
index 0000000..02d45ef
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/ThreeGPPNetworkElementTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement}.
+ */
+@SmallTest
+public class ThreeGPPNetworkElementTest {
+ private static final byte[][] TEST_NETWORK1_PLMN_BYTES =
+ new byte[][] { new byte[] {0x21, 0x63, 0x54},
+ new byte[] {0x43, (byte) 0x85, 0x76} };
+ private static final List<String> TEST_NETWORK1_PLMN_LIST = new ArrayList<>();
+ static {
+ TEST_NETWORK1_PLMN_LIST.add("123456");
+ TEST_NETWORK1_PLMN_LIST.add("345678");
+ }
+ private static final CellularNetwork TEST_NETWORK1 =
+ new CellularNetwork(TEST_NETWORK1_PLMN_LIST);
+
+ private static final byte[][] TEST_NETWORK2_PLMN_BYTES =
+ new byte[][] { new byte[] {(byte) 0x87, 0x29, 0x10},
+ new byte[] {0x62, (byte) 0xF5, 0x73} };
+ private static final List<String> TEST_NETWORK2_PLMN_LIST = new ArrayList<>();
+ static {
+ TEST_NETWORK2_PLMN_LIST.add("789012");
+ TEST_NETWORK2_PLMN_LIST.add("26537");
+ }
+ private static final CellularNetwork TEST_NETWORK2 =
+ new CellularNetwork(TEST_NETWORK2_PLMN_LIST);
+
+ /**
+ * Helper function for generating test data.
+ *
+ * @param version The GUD version number
+ * @param ieiList The array containing IEI data
+ * @return byte[]
+ * @throws IOException
+ */
+ private static byte[] getTestData(int version, byte[][] ieiList)
+ throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ int totalIeiSize = CellularNetworkTestUtil.getDataSize(ieiList);
+ stream.write((byte) version);
+ stream.write((byte) totalIeiSize);
+ for (byte[] iei : ieiList) {
+ stream.write(iei);
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseBufferWithEmptyBuffer() throws Exception {
+ ThreeGPPNetworkElement.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing an buffer contained
+ * an unsupported version number.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithUnsupportedVersionNumber() throws Exception {
+ byte[][] testIeiList = new byte[][] {
+ CellularNetworkTestUtil.formatPLMNListIEI(TEST_NETWORK1_PLMN_BYTES) };
+ byte[] testData = getTestData(1, testIeiList);
+ ThreeGPPNetworkElement.parse(ByteBuffer.wrap(testData));
+ }
+
+ /**
+ * Verify that Protocol will be thrown when parsing a truncated buffer (missing a
+ * byte at the end), which will cause a inconsistency between the length value and
+ * the buffer size.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithIncompleteData() throws Exception {
+ byte[][] testIeiList = new byte[][] {
+ CellularNetworkTestUtil.formatPLMNListIEI(TEST_NETWORK1_PLMN_BYTES) };
+ byte[] testData = getTestData(ThreeGPPNetworkElement.GUD_VERSION_1, testIeiList);
+ ThreeGPPNetworkElement.parse(ByteBuffer.wrap(testData, 0, testData.length - 1));
+ }
+
+ /**
+ * Verify that the expected ThreeGPPNetworkElement is returned when parsing a buffer contained
+ * the test data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData() throws Exception {
+ byte[][] testIeiList = new byte[][] {
+ CellularNetworkTestUtil.formatPLMNListIEI(TEST_NETWORK1_PLMN_BYTES),
+ CellularNetworkTestUtil.formatPLMNListIEI(TEST_NETWORK2_PLMN_BYTES) };
+ byte[] testData = getTestData(ThreeGPPNetworkElement.GUD_VERSION_1, testIeiList);
+
+ // Setup the expected ThreeGPPNetworkElement.
+ List<CellularNetwork> networkList = new ArrayList<>();
+ networkList.add(TEST_NETWORK1);
+ networkList.add(TEST_NETWORK2);
+ ThreeGPPNetworkElement expected = new ThreeGPPNetworkElement(networkList);
+
+ assertEquals(expected, ThreeGPPNetworkElement.parse(ByteBuffer.wrap(testData)));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java
new file mode 100644
index 0000000..407e7bf
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/VenueNameElementTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.hotspot2.anqp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.VenueNameElement}.
+ */
+@SmallTest
+public class VenueNameElementTest {
+ private static final String TEST_LANGUAGE = "en";
+ private static final Locale TEST_LOCALE = Locale.forLanguageTag(TEST_LANGUAGE);
+ private static final String TEST_VENUE_NAME1 = "Venue1";
+ private static final String TEST_VENUE_NAME2 = "Venue2";
+
+ /**
+ * Helper function for appending a Venue Name to an output stream.
+ *
+ * @param stream Stream to write to
+ * @param venue The venue name string
+ * @throws IOException
+ */
+ private void appendVenue(ByteArrayOutputStream stream, String venue) throws IOException {
+ byte[] venueBytes = venue.getBytes(StandardCharsets.UTF_8);
+ int length = I18Name.LANGUAGE_CODE_LENGTH + venue.length();
+ stream.write((byte) length);
+ stream.write(TEST_LANGUAGE.getBytes(StandardCharsets.US_ASCII));
+ stream.write(new byte[]{(byte) 0x0}); // Padding for language code.
+ stream.write(venueBytes);
+ }
+
+ /**
+ * Helper function for generating test data.
+ *
+ * @return byte[] of data
+ * @throws IOException
+ */
+ private byte[] getTestData(String[] names) throws IOException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ // Venue info data not currently used.
+ stream.write(new byte[VenueNameElement.VENUE_INFO_LENGTH]);
+ for (String name : names) {
+ appendVenue(stream, name);
+ }
+ return stream.toByteArray();
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ VenueNameElement.parse(ByteBuffer.allocate(0));
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[] {TEST_VENUE_NAME1}));
+ // Truncate a byte at the end.
+ buffer.limit(buffer.remaining() - 1);
+ VenueNameElement.parse(buffer);
+ }
+
+ /**
+ * Verify that a VenueNameElement with empty name list will be returned when parsing a buffer
+ * contained no venue name (only contained the venue info data).
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithEmptyVenueName() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[0]));
+ assertTrue(VenueNameElement.parse(buffer).getNames().isEmpty());
+ }
+ /**
+ * Verify that an expected VenueNameElement will be returned when parsing a buffer contained
+ * valid Venue Name data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithValidVenueNames() throws Exception {
+ // Setup expected element.
+ List<I18Name> nameList = new ArrayList<>();
+ nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_VENUE_NAME1));
+ nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, TEST_VENUE_NAME2));
+ VenueNameElement expectedElement = new VenueNameElement(nameList);
+
+ ByteBuffer buffer = ByteBuffer.wrap(
+ getTestData(new String[] {TEST_VENUE_NAME1, TEST_VENUE_NAME2}));
+ assertEquals(expectedElement, VenueNameElement.parse(buffer));
+ }
+
+ /**
+ * Verify that an expected VenueNameElement will be returned when parsing a buffer
+ * contained a venue name with the maximum length.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithMaxLengthVenueName() throws Exception {
+ // Venue name with maximum length.
+ byte[] textData = new byte[VenueNameElement.MAXIMUM_VENUE_NAME_LENGTH];
+ Arrays.fill(textData, (byte) 'a');
+ String text = new String(textData);
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[] {text}));
+
+ // Setup expected element.
+ List<I18Name> nameList = new ArrayList<>();
+ nameList.add(new I18Name(TEST_LANGUAGE, TEST_LOCALE, text));
+ VenueNameElement expectedElement = new VenueNameElement(nameList);
+
+ assertEquals(expectedElement, VenueNameElement.parse(buffer));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing a buffer contained a
+ * venue name that exceeds the maximum length.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithVenueNameLengthExceedMax() throws Exception {
+ byte[] textData = new byte[VenueNameElement.MAXIMUM_VENUE_NAME_LENGTH + 1];
+ Arrays.fill(textData, (byte) 'a');
+ String text = new String(textData);
+ ByteBuffer buffer = ByteBuffer.wrap(getTestData(new String[] {text}));
+ VenueNameElement.parse(buffer);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/CredentialTypeTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/CredentialTypeTest.java
new file mode 100644
index 0000000..14be73d
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/CredentialTypeTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.hotspot2.anqp.eap;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.CredentialType}.
+ */
+@SmallTest
+public class CredentialTypeTest {
+ private static final int TEST_TYPE = CredentialType.CREDENTIAL_TYPE_USIM;
+
+ /**
+ * Helper function for generating the test buffer.
+ *
+ * @return {@link ByteBuffer}
+ */
+ private ByteBuffer getTestBuffer() {
+ return ByteBuffer.wrap(new byte[] {(byte) TEST_TYPE});
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ CredentialType.parse(
+ ByteBuffer.wrap(new byte[0]), CredentialType.EXPECTED_LENGTH_VALUE, false);
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when the data length value is not the same
+ * as the expected
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithInvalidLength() throws Exception {
+ CredentialType.parse(getTestBuffer(), CredentialType.EXPECTED_LENGTH_VALUE - 1, false);
+ }
+
+ /**
+ * Verify that an expected CredentialType is returned when parsing the buffer for a
+ * non-tunneled EAP method.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferForNonTunneledEAPMethod() throws Exception {
+ CredentialType expected =
+ new CredentialType(AuthParam.PARAM_TYPE_CREDENTIAL_TYPE, TEST_TYPE);
+ CredentialType actual = CredentialType.parse(
+ getTestBuffer(), CredentialType.EXPECTED_LENGTH_VALUE, false);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Verify that an expected CredentialType is returned when parsing the buffer for a
+ * tunneled EAP method.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferForTunneledEAPMethod() throws Exception {
+ CredentialType expected = new CredentialType(
+ AuthParam.PARAM_TYPE_TUNNELED_EAP_METHOD_CREDENTIAL_TYPE, TEST_TYPE);
+ CredentialType actual = CredentialType.parse(
+ getTestBuffer(), CredentialType.EXPECTED_LENGTH_VALUE, true);
+ assertEquals(expected, actual);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/EAPMethodTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/EAPMethodTest.java
new file mode 100644
index 0000000..51434c2
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/EAPMethodTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.hotspot2.anqp.eap;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.wifi.EAPConstants;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.EAPMethod}.
+ */
+@SmallTest
+public class EAPMethodTest {
+ /**
+ * Setup basic test data - contained multiple parameters of the same type.
+ */
+ private static final byte[] TEST_DATA1_BYTES =
+ new byte[] {0x0B /* length */, 0x0D /* EAP_TLS */, 0x03 /* Auth Param Count */,
+ 0x05 /* CredentialType */, 0x01, 0x02 /* USIM */,
+ 0x05 /* CredentialType */, 0x01, 0x06 /* Certificate */,
+ 0x06 /* Tunneled CredentialType */, 0x01, 0x03 /* NFC */};
+ private static final Map<Integer, Set<AuthParam>> TEST_DATA1_AUTH_PARAMS =
+ new HashMap<>();
+ private static final Set<AuthParam> TEST_DATA1_CREDENTIAL_TYPE_PARAMS = new HashSet<>();
+ private static final Set<AuthParam> TEST_DATA1_TUNNELED_CREDENTIAL_TYPE_PARAMS =
+ new HashSet<>();
+ static {
+ TEST_DATA1_CREDENTIAL_TYPE_PARAMS.add(new CredentialType(
+ AuthParam.PARAM_TYPE_CREDENTIAL_TYPE, CredentialType.CREDENTIAL_TYPE_USIM));
+ TEST_DATA1_CREDENTIAL_TYPE_PARAMS.add(new CredentialType(
+ AuthParam.PARAM_TYPE_CREDENTIAL_TYPE, CredentialType.CREDENTIAL_TYPE_CERTIFICATE));
+
+ TEST_DATA1_TUNNELED_CREDENTIAL_TYPE_PARAMS.add(new CredentialType(
+ AuthParam.PARAM_TYPE_TUNNELED_EAP_METHOD_CREDENTIAL_TYPE,
+ CredentialType.CREDENTIAL_TYPE_NFC));
+
+ TEST_DATA1_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_CREDENTIAL_TYPE,
+ TEST_DATA1_CREDENTIAL_TYPE_PARAMS);
+ TEST_DATA1_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_TUNNELED_EAP_METHOD_CREDENTIAL_TYPE,
+ TEST_DATA1_TUNNELED_CREDENTIAL_TYPE_PARAMS);
+ }
+ private static final EAPMethod TEST_DATA1_EAP_METHOD = new EAPMethod(
+ EAPConstants.EAP_TLS, TEST_DATA1_AUTH_PARAMS);
+
+ /**
+ * Setup test data for testing an EAP Method containing all types of authentication parameters.
+ */
+ private static final byte[] TEST_DATA2_BYTES =
+ new byte[] {0x26 /* length */, 0x0D /* EAP_TLS */, 0x07,
+ // Expanded EAP Method
+ 0x01, 0x07, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78,
+ // Non-EAP Inner Auth Type
+ 0x02, 0x01, 0x03 /* AUTH_TYPE_MSCHAP */,
+ // Inner Auth EAP Method
+ 0x03, 0x01, 0x1D /* EAP_PEAP */,
+ // Expanded Inner EAP Method
+ 0x04, 0x07, 0x01, 0x23, 0x45, 0x56, 0x78, 0x56, 0x12,
+ // Credential Type
+ 0x05, 0x01, 0x02 /* USIM */,
+ // Tunneled Credential Type
+ 0x06, 0x01, 0x03 /* NFC */,
+ // Vendor Specific
+ (byte) 0xDD, 0x04, 0x12, 0x23, 0x45, 0x56};
+ private static final Map<Integer, Set<AuthParam>> TEST_DATA2_AUTH_PARAMS =
+ new HashMap<>();
+ private static final Set<AuthParam> TEST_DATA2_EXPANDED_EAP_METHOD_PARAMS = new HashSet<>();
+ private static final Set<AuthParam> TEST_DATA2_NON_EAP_INNER_AUTH_PARAMS = new HashSet<>();
+ private static final Set<AuthParam> TEST_DATA2_INNER_AUTH_EAP_PARAMS = new HashSet<>();
+ private static final Set<AuthParam> TEST_DATA2_EXPANDED_INNER_EAP_PARAMS = new HashSet<>();
+ private static final Set<AuthParam> TEST_DATA2_CREDENTIAL_TYPE_PARAMS = new HashSet<>();
+ private static final Set<AuthParam> TEST_DATA2_TUNNELED_CREDENTIAL_TYPE_PARAMS =
+ new HashSet<>();
+ private static final Set<AuthParam> TEST_DATA2_VENDOR_SPECIFIC_PARAMS = new HashSet<>();
+ static {
+ TEST_DATA2_EXPANDED_EAP_METHOD_PARAMS.add(new ExpandedEAPMethod(
+ AuthParam.PARAM_TYPE_EXPANDED_EAP_METHOD, 0x122334, 0x45566778L));
+ TEST_DATA2_NON_EAP_INNER_AUTH_PARAMS.add(new NonEAPInnerAuth(
+ NonEAPInnerAuth.AUTH_TYPE_MSCHAP));
+ TEST_DATA2_INNER_AUTH_EAP_PARAMS.add(new InnerAuthEAP(EAPConstants.EAP_PEAP));
+ TEST_DATA2_EXPANDED_INNER_EAP_PARAMS.add(new ExpandedEAPMethod(
+ AuthParam.PARAM_TYPE_EXPANDED_INNER_EAP_METHOD, 0x012345, 0x56785612L));
+ TEST_DATA2_CREDENTIAL_TYPE_PARAMS.add(new CredentialType(
+ AuthParam.PARAM_TYPE_CREDENTIAL_TYPE, CredentialType.CREDENTIAL_TYPE_USIM));
+ TEST_DATA2_TUNNELED_CREDENTIAL_TYPE_PARAMS.add(new CredentialType(
+ AuthParam.PARAM_TYPE_TUNNELED_EAP_METHOD_CREDENTIAL_TYPE,
+ CredentialType.CREDENTIAL_TYPE_NFC));
+ TEST_DATA2_VENDOR_SPECIFIC_PARAMS.add(new VendorSpecificAuth(
+ new byte[] {0x12, 0x23, 0x45, 0x56}));
+
+ TEST_DATA2_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_EXPANDED_EAP_METHOD,
+ TEST_DATA2_EXPANDED_EAP_METHOD_PARAMS);
+ TEST_DATA2_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_NON_EAP_INNER_AUTH_TYPE,
+ TEST_DATA2_NON_EAP_INNER_AUTH_PARAMS);
+ TEST_DATA2_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_INNER_AUTH_EAP_METHOD_TYPE,
+ TEST_DATA2_INNER_AUTH_EAP_PARAMS);
+ TEST_DATA2_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_EXPANDED_INNER_EAP_METHOD,
+ TEST_DATA2_EXPANDED_INNER_EAP_PARAMS);
+ TEST_DATA2_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_CREDENTIAL_TYPE,
+ TEST_DATA2_CREDENTIAL_TYPE_PARAMS);
+ TEST_DATA2_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_TUNNELED_EAP_METHOD_CREDENTIAL_TYPE,
+ TEST_DATA2_TUNNELED_CREDENTIAL_TYPE_PARAMS);
+ TEST_DATA2_AUTH_PARAMS.put(AuthParam.PARAM_TYPE_VENDOR_SPECIFIC,
+ TEST_DATA2_VENDOR_SPECIFIC_PARAMS);
+ }
+ private static final EAPMethod TEST_DATA2_EAP_METHOD = new EAPMethod(
+ EAPConstants.EAP_TLS, TEST_DATA2_AUTH_PARAMS);
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ EAPMethod.parse(ByteBuffer.wrap(new byte[0]));
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when parsing a truncated buffer
+ * (missing a byte at the end).
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ ByteBuffer buffer = ByteBuffer.wrap(TEST_DATA1_BYTES, 0, TEST_DATA1_BYTES.length - 1);
+ EAPMethod.parse(buffer);
+ }
+
+ /**
+ * Verify that the expected EAPMethod is return when parsing a buffer contained
+ * {@link #TEST_DATA1_BYTES}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData1() throws Exception {
+ assertEquals(TEST_DATA1_EAP_METHOD, EAPMethod.parse(ByteBuffer.wrap(TEST_DATA1_BYTES)));
+ }
+
+ /**
+ * Verify that the expected EAPMethod is return when parsing a buffer contained
+ * {@link #TEST_DATA2_BYTES}.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithTestData2() throws Exception {
+ assertEquals(TEST_DATA2_EAP_METHOD, EAPMethod.parse(ByteBuffer.wrap(TEST_DATA2_BYTES)));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethodTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethodTest.java
new file mode 100644
index 0000000..8f114b2
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/ExpandedEAPMethodTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.hotspot2.anqp.eap;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.ExpandedEAPMethod}.
+ */
+@SmallTest
+public class ExpandedEAPMethodTest {
+ private static final int TEST_VENDOR_ID = 0x123456;
+ private static final long TEST_VENDOR_TYPE = 0x23456523;
+ private static final byte[] TEST_DATA_BYTES =
+ new byte[] {0x12, 0x34, 0x56, 0x23, 0x45, 0x65, 0x23};
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ ExpandedEAPMethod.parse(
+ ByteBuffer.wrap(new byte[0]), ExpandedEAPMethod.EXPECTED_LENGTH_VALUE, false);
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when the data length is not the expected
+ * length.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithInvalidLength() throws Exception {
+ ExpandedEAPMethod.parse(ByteBuffer.wrap(TEST_DATA_BYTES),
+ ExpandedEAPMethod.EXPECTED_LENGTH_VALUE - 1, false);
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing a truncated buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseBufferWithTruncatedBuffer() throws Exception {
+ ExpandedEAPMethod.parse(ByteBuffer.wrap(TEST_DATA_BYTES, 0, TEST_DATA_BYTES.length - 1),
+ ExpandedEAPMethod.EXPECTED_LENGTH_VALUE, false);
+ }
+
+ /**
+ * Verify that an expected ExpandedEAPMethod is returned when parsing the buffer for a
+ * non-inner EAP method.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferForNonInnerEAPMethod() throws Exception {
+ ExpandedEAPMethod expected = new ExpandedEAPMethod(
+ AuthParam.PARAM_TYPE_EXPANDED_EAP_METHOD, TEST_VENDOR_ID, TEST_VENDOR_TYPE);
+ ExpandedEAPMethod actual = ExpandedEAPMethod.parse(
+ ByteBuffer.wrap(TEST_DATA_BYTES), ExpandedEAPMethod.EXPECTED_LENGTH_VALUE, false);
+ assertEquals(expected, actual);
+ }
+
+ /**
+ * Verify that an expected CredentialType is returned when parsing the buffer for a
+ * inner EAP method.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferForTunneledEAPMethod() throws Exception {
+ ExpandedEAPMethod expected = new ExpandedEAPMethod(
+ AuthParam.PARAM_TYPE_EXPANDED_INNER_EAP_METHOD, TEST_VENDOR_ID, TEST_VENDOR_TYPE);
+ ExpandedEAPMethod actual = ExpandedEAPMethod.parse(
+ ByteBuffer.wrap(TEST_DATA_BYTES), ExpandedEAPMethod.EXPECTED_LENGTH_VALUE, true);
+ assertEquals(expected, actual);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAPTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAPTest.java
new file mode 100644
index 0000000..a4a813b
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/InnerAuthEAPTest.java
@@ -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.
+ */
+
+package com.android.server.wifi.hotspot2.anqp.eap;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.wifi.EAPConstants;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.InnerAuthEAP}.
+ */
+@SmallTest
+public class InnerAuthEAPTest {
+ private static final int TEST_EAP_METHOD_ID = EAPConstants.EAP_TTLS;
+
+ /**
+ * Helper function for generating the test buffer.
+ *
+ * @return {@link ByteBuffer}
+ */
+ private ByteBuffer getTestBuffer() {
+ return ByteBuffer.wrap(new byte[] {(byte) TEST_EAP_METHOD_ID});
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ InnerAuthEAP.parse(ByteBuffer.wrap(new byte[0]), InnerAuthEAP.EXPECTED_LENGTH_VALUE);
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when the data length is not the expected
+ * length.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithInvalidLength() throws Exception {
+ InnerAuthEAP.parse(getTestBuffer(), InnerAuthEAP.EXPECTED_LENGTH_VALUE - 1);
+ }
+
+ /**
+ * Verify that an expected InnerAuthEAP is returned when parsing a buffer contained
+ * the expected EAP method ID.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBuffer() throws Exception {
+ InnerAuthEAP expected = new InnerAuthEAP(TEST_EAP_METHOD_ID);
+ InnerAuthEAP actual =
+ InnerAuthEAP.parse(getTestBuffer(), InnerAuthEAP.EXPECTED_LENGTH_VALUE);
+ assertEquals(expected, actual);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuthTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuthTest.java
new file mode 100644
index 0000000..9770321
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/NonEAPInnerAuthTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.hotspot2.anqp.eap;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.net.ProtocolException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.NonEAPInnerAuth}.
+ */
+@SmallTest
+public class NonEAPInnerAuthTest {
+ private static final int TEST_AUTH_TYPE = NonEAPInnerAuth.AUTH_TYPE_MSCHAP;
+
+ /**
+ * Helper function for generating the test buffer.
+ *
+ * @return {@link ByteBuffer}
+ */
+ private ByteBuffer getTestBuffer() {
+ return ByteBuffer.wrap(new byte[] {(byte) TEST_AUTH_TYPE});
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ NonEAPInnerAuth.parse(ByteBuffer.wrap(new byte[0]), NonEAPInnerAuth.EXPECTED_LENGTH_VALUE);
+ }
+
+ /**
+ * Verify that ProtocolException will be thrown when the data length is not the expected
+ * length.
+ *
+ * @throws Exception
+ */
+ @Test(expected = ProtocolException.class)
+ public void parseBufferWithInvalidLength() throws Exception {
+ NonEAPInnerAuth.parse(getTestBuffer(), NonEAPInnerAuth.EXPECTED_LENGTH_VALUE - 1);
+ }
+
+ /**
+ * Verify that an expected NonEAPInnerAuth is returned when parsing a buffer contained
+ * the expected auth type.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBuffer() throws Exception {
+ NonEAPInnerAuth expected = new NonEAPInnerAuth(TEST_AUTH_TYPE);
+ NonEAPInnerAuth actual =
+ NonEAPInnerAuth.parse(getTestBuffer(), NonEAPInnerAuth.EXPECTED_LENGTH_VALUE);
+ assertEquals(expected, actual);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuthTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuthTest.java
new file mode 100644
index 0000000..dbb2d8f
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/anqp/eap/VendorSpecificAuthTest.java
@@ -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.
+ */
+
+package com.android.server.wifi.hotspot2.anqp.eap;
+
+import static org.junit.Assert.assertEquals;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.hotspot2.anqp.eap.VendorSpecificAuth}.
+ */
+@SmallTest
+public class VendorSpecificAuthTest {
+ private static final byte[] TEST_DATA = new byte[] {0x12, 0x34, 0x45, 0x56};
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing from an empty buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseEmptyBuffer() throws Exception {
+ VendorSpecificAuth.parse(ByteBuffer.wrap(new byte[0]), 1);
+ }
+
+ /**
+ * Verify that BufferUnderflowException will be thrown when parsing from a truncated buffer.
+ *
+ * @throws Exception
+ */
+ @Test(expected = BufferUnderflowException.class)
+ public void parseTruncatedBuffer() throws Exception {
+ VendorSpecificAuth.parse(
+ ByteBuffer.wrap(TEST_DATA, 0, TEST_DATA.length - 1), TEST_DATA.length);
+ }
+
+ /**
+ * Verify that a VendorSpecificAuth with a empty data array is returned when parsing
+ * a zero byte from a buffer.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBufferWithZeroLength() throws Exception {
+ VendorSpecificAuth expected = new VendorSpecificAuth(new byte[0]);
+ assertEquals(expected, VendorSpecificAuth.parse(ByteBuffer.wrap(TEST_DATA), 0));
+ }
+
+ /**
+ * Verify that an expected VendorSpecificAuth is returned when parsing a buffer contained
+ * the expected data.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseBuffer() throws Exception {
+ VendorSpecificAuth expected = new VendorSpecificAuth(TEST_DATA);
+ VendorSpecificAuth actual =
+ VendorSpecificAuth.parse(ByteBuffer.wrap(TEST_DATA), TEST_DATA.length);
+ assertEquals(expected, actual);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/TlvBufferUtilsTest.java b/tests/wifitests/src/com/android/server/wifi/nan/TlvBufferUtilsTest.java
deleted file mode 100644
index 5c83185..0000000
--- a/tests/wifitests/src/com/android/server/wifi/nan/TlvBufferUtilsTest.java
+++ /dev/null
@@ -1,234 +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.nan;
-
-import static org.hamcrest.core.IsEqual.equalTo;
-
-import android.net.wifi.nan.TlvBufferUtils;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ErrorCollector;
-import org.junit.rules.ExpectedException;
-
-/**
- * Unit test harness for WifiNanManager class.
- */
-@SmallTest
-public class TlvBufferUtilsTest {
- @Rule
- public ErrorCollector collector = new ErrorCollector();
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- /*
- * TlvBufferUtils Tests
- */
-
- @Test
- public void testTlvBuild() {
- TlvBufferUtils.TlvConstructor tlv11 = new TlvBufferUtils.TlvConstructor(1, 1);
- tlv11.allocate(15);
- tlv11.putByte(0, (byte) 2);
- tlv11.putByteArray(2, new byte[] {
- 0, 1, 2 });
-
- collector.checkThat("tlv11-correct-construction",
- utilAreArraysEqual(tlv11.getArray(), tlv11.getActualLength(), new byte[] {
- 0, 1, 2, 2, 3, 0, 1, 2 }, 8),
- equalTo(true));
-
- TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1);
- tlv01.allocate(15);
- tlv01.putByte(0, (byte) 2);
- tlv01.putByteArray(2, new byte[] {
- 0, 1, 2 });
-
- collector.checkThat("tlv01-correct-construction",
- utilAreArraysEqual(tlv01.getArray(), tlv01.getActualLength(), new byte[] {
- 1, 2, 3, 0, 1, 2 }, 6),
- equalTo(true));
- }
-
- @Test
- public void testTlvIterate() {
- TlvBufferUtils.TlvConstructor tlv22 = new TlvBufferUtils.TlvConstructor(2, 2);
- tlv22.allocate(18);
- tlv22.putInt(0, 2);
- tlv22.putShort(2, (short) 3);
- tlv22.putZeroLengthElement(55);
-
- TlvBufferUtils.TlvIterable tlv22It = new TlvBufferUtils.TlvIterable(2, 2, tlv22.getArray(),
- tlv22.getActualLength());
- int count = 0;
- for (TlvBufferUtils.TlvElement tlv : tlv22It) {
- if (count == 0) {
- collector.checkThat("tlv22-correct-iteration-mType", tlv.mType, equalTo(0));
- collector.checkThat("tlv22-correct-iteration-mLength", tlv.mLength, equalTo(4));
- collector.checkThat("tlv22-correct-iteration-DATA", tlv.getInt(), equalTo(2));
- } else if (count == 1) {
- collector.checkThat("tlv22-correct-iteration-mType", tlv.mType, equalTo(2));
- collector.checkThat("tlv22-correct-iteration-mLength", tlv.mLength, equalTo(2));
- collector.checkThat("tlv22-correct-iteration-DATA", (int) tlv.getShort(),
- equalTo(3));
- } else if (count == 2) {
- collector.checkThat("tlv22-correct-iteration-mType", tlv.mType, equalTo(55));
- collector.checkThat("tlv22-correct-iteration-mLength", tlv.mLength, equalTo(0));
- } else {
- collector.checkThat("Invalid number of iterations in loop - tlv22", true,
- equalTo(false));
- }
- ++count;
- }
- if (count != 3) {
- collector.checkThat("Invalid number of iterations outside loop - tlv22", true,
- equalTo(false));
- }
-
- TlvBufferUtils.TlvConstructor tlv02 = new TlvBufferUtils.TlvConstructor(0, 2);
- tlv02.allocate(15);
- tlv02.putByte(0, (byte) 2);
- tlv02.putString(0, "ABC");
-
- TlvBufferUtils.TlvIterable tlv02It = new TlvBufferUtils.TlvIterable(0, 2, tlv02.getArray(),
- tlv02.getActualLength());
- count = 0;
- for (TlvBufferUtils.TlvElement tlv : tlv02It) {
- if (count == 0) {
- collector.checkThat("tlv02-correct-iteration-mLength", tlv.mLength, equalTo(1));
- 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"),
- 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));
- }
- }
-
- @Test
- public void testTlvInvalidSizeT1L0() {
- thrown.expect(IllegalArgumentException.class);
- TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 0);
- }
-
- @Test
- public void testTlvInvalidSizeTm3L2() {
- thrown.expect(IllegalArgumentException.class);
- TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(-3, 2);
- }
-
- @Test
- public void testTlvInvalidSizeT1Lm2() {
- thrown.expect(IllegalArgumentException.class);
- TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, -2);
- }
-
- @Test
- public void testTlvInvalidSizeT1L3() {
- thrown.expect(IllegalArgumentException.class);
- TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 3);
- }
-
- @Test
- public void testTlvInvalidSizeT3L1() {
- thrown.expect(IllegalArgumentException.class);
- TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(3, 1);
- }
-
- @Test
- 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
- 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
- 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
- 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
- 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);
- }
-
- /*
- * Utilities
- */
-
- private static boolean utilAreArraysEqual(byte[] x, int xLength, byte[] y, int yLength) {
- if (xLength != yLength) {
- return false;
- }
-
- if (x != null && y != null) {
- for (int i = 0; i < xLength; ++i) {
- if (x[i] != y[i]) {
- return false;
- }
- }
- } else if (xLength != 0) {
- return false; // invalid != invalid
- }
-
- return true;
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalMock.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalMock.java
deleted file mode 100644
index 4f3ba4d..0000000
--- a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalMock.java
+++ /dev/null
@@ -1,97 +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.nan;
-
-import java.lang.reflect.Field;
-
-/**
- * Mock class for NAN HAL. Provides access to HAL API and to callbacks. To
- * extend:
- * <ul>
- * <li>HAL API: create a {@code public void} method which takes any fixed
- * arguments (e.g. a {@code short transactionId} and a second argument to
- * provide the rest of the argument as a JSON string: {@code String jsonArgs}.
- * <li>Callbacks from HAL: create a {@code public static native} function which
- * is used to trigger the callback from the test harness. The arguments are
- * similar to the HAL API arguments.
- * </ul>
- */
-public class WifiNanHalMock {
- public void getCapabilitiesHalMockNative(short transactionId) {
- throw new IllegalStateException("Please mock this class!");
- }
-
- public void enableHalMockNative(short transactionId, String jsonArgs) {
- throw new IllegalStateException("Please mock this class!");
- }
-
- public void disableHalMockNative(short transactionId) {
- throw new IllegalStateException("Please mock this class!");
- }
-
- public void publishHalMockNative(short transactionId, String jsonArgs) {
- throw new IllegalStateException("Please mock this class!");
- }
-
- public void publishCancelHalMockNative(short transactionId, String jsonArgs) {
- throw new IllegalStateException("Please mock this class!");
- }
-
- public void subscribeHalMockNative(short transactionId, String jsonArgs) {
- throw new IllegalStateException("Please mock this class!");
- }
-
- public void subscribeCancelHalMockNative(short transactionId, String jsonArgs) {
- throw new IllegalStateException("Please mock this class!");
- }
-
- public void transmitFollowupHalMockNative(short transactionId, String jsonArgs) {
- throw new IllegalStateException("Please mock this class!");
- }
-
- /*
- * trigger callbacks - called by test harness with arguments passed by JSON
- * string.
- */
-
- public static native void callNotifyResponse(short transactionId, String jsonArgs);
-
- public static native void callPublishTerminated(String jsonArgs);
-
- public static native void callSubscribeTerminated(String jsonArgs);
-
- public static native void callFollowup(String jsonArgs);
-
- public static native void callMatch(String jsonArgs);
-
- public static native void callDiscEngEvent(String jsonArgs);
-
- public static native void callDisabled(String jsonArgs);
-
- /**
- * initialize NAN mock
- */
- private static native int initNanHalMock();
-
- public static void initNanHalMockLibrary() throws Exception {
- Field field = WifiNanNative.class.getDeclaredField("sNanNativeInit");
- field.setAccessible(true);
- field.setBoolean(null, 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
deleted file mode 100644
index 536c924..0000000
--- a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanHalTest.java
+++ /dev/null
@@ -1,762 +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.nan;
-
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-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.TlvBufferUtils;
-import android.net.wifi.nan.WifiNanSessionListener;
-import android.os.Bundle;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.server.wifi.HalMockUtils;
-import com.android.server.wifi.WifiNative;
-
-import libcore.util.HexEncoding;
-
-import org.json.JSONException;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ErrorCollector;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.lang.reflect.Field;
-import java.util.Arrays;
-
-/**
- * Unit test harness for WifiNanNative + JNI code interfacing to the HAL.
- */
-@SmallTest
-public class WifiNanHalTest {
- private WifiNanNative mDut = WifiNanNative.getInstance();
- private ArgumentCaptor<String> mArgs = ArgumentCaptor.forClass(String.class);
-
- @Mock
- private WifiNanHalMock mNanHalMock;
- @Mock private WifiNanStateManager mNanStateManager;
-
- @Rule
- public ErrorCollector collector = new ErrorCollector();
-
- @Before
- public void setup() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- HalMockUtils.initHalMockLibrary();
- WifiNanHalMock.initNanHalMockLibrary();
- WifiNanNative.initNanHandlersNative(WifiNative.class, WifiNative.sWlan0Index);
- HalMockUtils.setHalMockObject(mNanHalMock);
- installMockNanStateManager(mNanStateManager);
- }
-
- @Test
- public void testEnableWith5g() throws JSONException {
- final short transactionId = 2346;
- final int clusterLow = 23;
- final int clusterHigh = 126;
- final int masterPref = 234;
- final boolean enable5g = true;
-
- testEnable(transactionId, clusterLow, clusterHigh, masterPref, enable5g);
- }
-
- @Test
- public void testEnableWithout5g() throws JSONException {
- final short transactionId = 1296;
- final int clusterLow = 17;
- final int clusterHigh = 197;
- final int masterPref = 33;
- final boolean enable5g = false;
-
- testEnable(transactionId, clusterLow, clusterHigh, masterPref, enable5g);
- }
-
- @Test
- public void testDisable() {
- final short transactionId = 5478;
-
- mDut.disable(transactionId);
-
- verify(mNanHalMock).disableHalMockNative(transactionId);
- }
-
- @Test
- public void testPublishUnsolicited() throws JSONException {
- final short transactionId = 55;
- final int publishId = 23;
- final String serviceName = "some-service-name";
- final String ssi = "some much longer and more arbitrary data";
- final int publishCount = 7;
- final int publishTtl = 66;
-
- TlvBufferUtils.TlvConstructor tlvTx = new TlvBufferUtils.TlvConstructor(0, 1);
- tlvTx.allocate(150).putByte(0, (byte) 10).putInt(0, 100).putString(0, "some string")
- .putZeroLengthElement(0);
-
- TlvBufferUtils.TlvConstructor tlvRx = new TlvBufferUtils.TlvConstructor(0, 1);
- 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);
- }
-
- @Test
- public void testPublishSolicited() throws JSONException {
- final short transactionId = 45;
- final int publishId = 17;
- final String serviceName = "some-service-name-or-another";
- final String ssi = "some much longer arbitrary data";
- final int publishCount = 32;
- final int publishTtl = 33;
-
- TlvBufferUtils.TlvConstructor tlvTx = new TlvBufferUtils.TlvConstructor(0, 1);
- tlvTx.allocate(150).putByte(0, (byte) 10).putInt(0, 100).putString(0, "some string")
- .putZeroLengthElement(0);
-
- TlvBufferUtils.TlvConstructor tlvRx = new TlvBufferUtils.TlvConstructor(0, 1);
- 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);
- }
-
- @Test
- public void testPublishCancel() throws JSONException {
- final short transactionId = 12;
- final int publishId = 15;
-
- mDut.stopPublish(transactionId, publishId);
-
- verify(mNanHalMock).publishCancelHalMockNative(eq(transactionId), mArgs.capture());
-
- Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
-
- collector.checkThat("publish_id", argsData.getInt("publish_id"), equalTo(publishId));
- }
-
- @Test
- public void testSubscribePassive() throws JSONException {
- final short transactionId = 45;
- final int subscribeId = 17;
- final String serviceName = "some-service-name-or-another";
- final String ssi = "some much longer arbitrary data";
- final int subscribeCount = 32;
- final int subscribeTtl = 33;
-
- TlvBufferUtils.TlvConstructor tlvTx = new TlvBufferUtils.TlvConstructor(0, 1);
- tlvTx.allocate(150).putByte(0, (byte) 10).putInt(0, 100).putString(0, "some string")
- .putZeroLengthElement(0);
-
- TlvBufferUtils.TlvConstructor tlvRx = new TlvBufferUtils.TlvConstructor(0, 1);
- 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);
- }
-
- @Test
- public void testSubscribeActive() throws JSONException {
- final short transactionId = 45;
- final int subscribeId = 17;
- final String serviceName = "some-service-name-or-another";
- final String ssi = "some much longer arbitrary data";
- final int subscribeCount = 32;
- final int subscribeTtl = 33;
-
- TlvBufferUtils.TlvConstructor tlvTx = new TlvBufferUtils.TlvConstructor(0, 1);
- tlvTx.allocate(150).putByte(0, (byte) 10).putInt(0, 100).putString(0, "some string")
- .putZeroLengthElement(0);
-
- TlvBufferUtils.TlvConstructor tlvRx = new TlvBufferUtils.TlvConstructor(0, 1);
- 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);
- }
-
- @Test
- public void testSubscribeCancel() throws JSONException {
- final short transactionId = 12;
- final int subscribeId = 15;
-
- mDut.stopSubscribe(transactionId, subscribeId);
-
- verify(mNanHalMock).subscribeCancelHalMockNative(eq(transactionId), mArgs.capture());
-
- Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
-
- collector.checkThat("subscribe_id", argsData.getInt("subscribe_id"), equalTo(subscribeId));
- }
-
- @Test
- public void testSendMessage() throws JSONException {
- final short transactionId = 45;
- final int pubSubId = 22;
- final int reqInstanceId = 11;
- final byte[] peer = HexEncoding.decode("000102030405".toCharArray(), false);
- final String msg = "Hello there - how are you doing?";
-
- mDut.sendMessage(transactionId, pubSubId, reqInstanceId, peer, msg.getBytes(),
- msg.length());
-
- verify(mNanHalMock).transmitFollowupHalMockNative(eq(transactionId), mArgs.capture());
-
- Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
-
- collector.checkThat("publish_subscribe_id", argsData.getInt("publish_subscribe_id"),
- equalTo(pubSubId));
- collector.checkThat("requestor_instance_id", argsData.getInt("requestor_instance_id"),
- equalTo(reqInstanceId));
- collector.checkThat("addr", argsData.getByteArray("addr"), equalTo(peer));
- collector.checkThat("priority", argsData.getInt("priority"), equalTo(0));
- collector.checkThat("dw_or_faw", argsData.getInt("dw_or_faw"), equalTo(0));
- collector.checkThat("service_specific_info_len",
- argsData.getInt("service_specific_info_len"), equalTo(msg.length()));
- collector.checkThat("service_specific_info", argsData.getByteArray("service_specific_info"),
- equalTo(msg.getBytes()));
- }
-
- @Test
- public void testNotifyCapabilities() throws JSONException {
- final short transactionId = 23;
- final int max_concurrent_nan_clusters = 1;
- final int max_publishes = 2;
- final int max_subscribes = 3;
- final int max_service_name_len = 4;
- final int max_match_filter_len = 5;
- final int max_total_match_filter_len = 6;
- final int max_service_specific_info_len = 7;
- final int max_vsa_data_len = 8;
- final int max_mesh_data_len = 9;
- final int max_ndi_interfaces = 10;
- final int max_ndp_sessions = 11;
- final int max_app_info_len = 12;
-
- ArgumentCaptor<WifiNanNative.Capabilities> capabilitiesCapture = ArgumentCaptor
- .forClass(WifiNanNative.Capabilities.class);
-
- Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_SUCCESS);
- args.putInt("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_GET_CAPABILITIES);
-
- args.putInt("body.nan_capabilities.max_concurrent_nan_clusters",
- max_concurrent_nan_clusters);
- args.putInt("body.nan_capabilities.max_publishes", max_publishes);
- args.putInt("body.nan_capabilities.max_subscribes", max_subscribes);
- args.putInt("body.nan_capabilities.max_service_name_len", max_service_name_len);
- args.putInt("body.nan_capabilities.max_match_filter_len", max_match_filter_len);
- args.putInt("body.nan_capabilities.max_total_match_filter_len", max_total_match_filter_len);
- args.putInt("body.nan_capabilities.max_service_specific_info_len",
- max_service_specific_info_len);
- args.putInt("body.nan_capabilities.max_vsa_data_len", max_vsa_data_len);
- args.putInt("body.nan_capabilities.max_mesh_data_len", max_mesh_data_len);
- 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);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onCapabilitiesUpdate(eq(transactionId),
- capabilitiesCapture.capture());
- WifiNanNative.Capabilities capabilities = capabilitiesCapture.getValue();
- collector.checkThat("max_concurrent_nan_clusters", capabilities.maxConcurrentNanClusters,
- equalTo(max_concurrent_nan_clusters));
- collector.checkThat("max_publishes", capabilities.maxPublishes, equalTo(max_publishes));
- collector.checkThat("max_subscribes", capabilities.maxSubscribes, equalTo(max_subscribes));
- collector.checkThat("max_service_name_len", capabilities.maxServiceNameLen,
- equalTo(max_service_name_len));
- collector.checkThat("max_match_filter_len", capabilities.maxMatchFilterLen,
- equalTo(max_match_filter_len));
- collector.checkThat("max_total_match_filter_len", capabilities.maxTotalMatchFilterLen,
- equalTo(max_total_match_filter_len));
- collector.checkThat("max_service_specific_info_len", capabilities.maxServiceSpecificInfoLen,
- equalTo(max_service_specific_info_len));
- collector.checkThat("max_vsa_data_len", capabilities.maxVsaDataLen,
- equalTo(max_vsa_data_len));
- collector.checkThat("max_mesh_data_len", capabilities.maxMeshDataLen,
- equalTo(max_mesh_data_len));
- collector.checkThat("max_ndi_interfaces", capabilities.maxNdiInterfaces,
- equalTo(max_ndi_interfaces));
- collector.checkThat("max_ndp_sessions", capabilities.maxNdpSessions,
- equalTo(max_ndp_sessions));
- collector.checkThat("max_app_info_len", capabilities.maxAppInfoLen,
- equalTo(max_app_info_len));
- }
-
- @Test
- public void testNotifyResponseConfigSuccess() throws JSONException {
- final short transactionId = 23;
-
- Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_SUCCESS);
- args.putInt("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_ENABLED);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onConfigCompleted(transactionId);
- }
-
- @Test
- public void testNotifyResponseConfigFail() throws JSONException {
- final short transactionId = 23;
-
- Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_INVALID_BAND_CONFIG_FLAGS);
- args.putInt("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_ENABLED);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onConfigFailed(transactionId,
- WifiNanSessionListener.FAIL_REASON_INVALID_ARGS);
- }
-
- @Test
- public void testNotifyResponsePublishSuccess() throws JSONException {
- final short transactionId = 23;
- final int publishId = 127;
-
- Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_SUCCESS);
- args.putInt("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_PUBLISH);
- args.putInt("body.publish_response.publish_id", publishId);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onPublishSuccess(transactionId, publishId);
- }
-
- @Test
- public void testNotifyResponsePublishFail() throws JSONException {
- final short transactionId = 23;
- final int publishId = 127;
-
- Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_NO_SPACE_AVAILABLE);
- args.putInt("value", 57);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_PUBLISH);
- args.putInt("body.publish_response.publish_id", publishId);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onPublishFail(transactionId,
- WifiNanSessionListener.FAIL_REASON_NO_RESOURCES);
- }
-
- @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);
- args.putInt("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_PUBLISH_CANCEL);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verifyZeroInteractions(mNanStateManager);
- }
-
- @Test
- public void testNotifyResponseSubscribeSuccess() throws JSONException {
- final short transactionId = 17;
- final int subscribeId = 198;
-
- Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_SUCCESS);
- args.putInt("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_SUBSCRIBE);
- args.putInt("body.subscribe_response.subscribe_id", subscribeId);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onSubscribeSuccess(transactionId, subscribeId);
- }
-
- @Test
- public void testNotifyResponseSubscribeFail() throws JSONException {
- final short transactionId = 17;
- final int subscribeId = 198;
-
- Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_DE_FAILURE);
- args.putInt("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_SUBSCRIBE);
- args.putInt("body.subscribe_response.subscribe_id", subscribeId);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onSubscribeFail(transactionId,
- WifiNanSessionListener.FAIL_REASON_OTHER);
- }
-
- @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("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_SUBSCRIBE_CANCEL);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verifyZeroInteractions(mNanStateManager);
- }
-
- @Test
- public void testNotifyResponseTransmitFollowupSuccess() throws JSONException {
- final short transactionId = 23;
-
- Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_SUCCESS);
- args.putInt("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_TRANSMIT_FOLLOWUP);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onMessageSendSuccess(transactionId);
- }
-
- @Test
- public void testNotifyResponseTransmitFollowupFail() throws JSONException {
- final short transactionId = 45;
-
- Bundle args = new Bundle();
- args.putInt("status", WifiNanNative.NAN_STATUS_TIMEOUT);
- args.putInt("value", 0);
- args.putInt("response_type", WifiNanNative.NAN_RESPONSE_TRANSMIT_FOLLOWUP);
-
- WifiNanHalMock.callNotifyResponse(transactionId,
- HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onMessageSendFail(transactionId,
- WifiNanSessionListener.FAIL_REASON_OTHER);
- }
-
- @Test
- public void testPublishTerminatedDone() throws JSONException {
- final int publishId = 167;
-
- Bundle args = new Bundle();
- args.putInt("publish_id", publishId);
- args.putInt("reason", WifiNanNative.NAN_TERMINATED_REASON_COUNT_REACHED);
-
- WifiNanHalMock.callPublishTerminated(HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onPublishTerminated(publishId,
- WifiNanSessionListener.TERMINATE_REASON_DONE);
- }
-
- @Test
- public void testSubscribeTerminatedFail() throws JSONException {
- final int subscribeId = 167;
-
- Bundle args = new Bundle();
- args.putInt("subscribe_id", subscribeId);
- args.putInt("reason", WifiNanNative.NAN_TERMINATED_REASON_FAILURE);
-
- WifiNanHalMock.callSubscribeTerminated(HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onSubscribeTerminated(subscribeId,
- WifiNanSessionListener.TERMINATE_REASON_FAIL);
- }
-
- @Test
- public void testFollowup() throws JSONException {
- final int pubSubId = 236;
- final int reqInstanceId = 57;
- final byte[] peer = HexEncoding.decode("0A0B0C0D0E0F".toCharArray(), false);
- final String message = "this is some message received from some peer - hello!";
-
- Bundle args = new Bundle();
- args.putInt("publish_subscribe_id", pubSubId);
- args.putInt("requestor_instance_id", reqInstanceId);
- args.putByteArray("addr", peer);
- args.putInt("dw_or_faw", 0);
- args.putInt("service_specific_info_len", message.length());
- args.putByteArray("service_specific_info", message.getBytes());
-
- WifiNanHalMock.callFollowup(HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onMessageReceived(pubSubId, reqInstanceId, peer,
- message.getBytes(), message.length());
- }
-
- @Test
- public void testMatch() throws JSONException {
- final int pubSubId = 287;
- final int reqInstanceId = 98;
- final byte[] peer = HexEncoding.decode("010203040506".toCharArray(), false);
- final String ssi = "some service specific info - really arbitrary";
- final String filter = "most likely binary - but faking here with some string data";
-
- Bundle args = new Bundle();
- args.putInt("publish_subscribe_id", pubSubId);
- args.putInt("requestor_instance_id", reqInstanceId);
- args.putByteArray("addr", peer);
- args.putInt("service_specific_info_len", ssi.length());
- args.putByteArray("service_specific_info", ssi.getBytes());
- args.putInt("sdf_match_filter_len", filter.length());
- args.putByteArray("sdf_match_filter", filter.getBytes());
-
- WifiNanHalMock.callMatch(HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onMatch(pubSubId, reqInstanceId, peer, ssi.getBytes(),
- ssi.length(), filter.getBytes(), filter.length());
- }
-
- @Test
- public void testDiscoveryInterfaceChange() throws JSONException {
- final byte[] mac = HexEncoding.decode("060504030201".toCharArray(), false);
-
- Bundle args = new Bundle();
- args.putInt("event_type", WifiNanNative.NAN_EVENT_ID_DISC_MAC_ADDR);
- args.putByteArray("data", mac);
-
- WifiNanHalMock.callDiscEngEvent(HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onInterfaceAddressChange(mac);
- }
-
- @Test
- public void testClusterChange() throws JSONException {
- final byte[] mac = HexEncoding.decode("060504030201".toCharArray(), false);
-
- Bundle args = new Bundle();
- args.putInt("event_type", WifiNanNative.NAN_EVENT_ID_JOINED_CLUSTER);
- args.putByteArray("data", mac);
-
- WifiNanHalMock.callDiscEngEvent(HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_JOINED,
- mac);
- }
-
- @Test
- public void testDisabled() throws JSONException {
- Bundle args = new Bundle();
- args.putInt("reason", WifiNanNative.NAN_STATUS_DE_FAILURE);
-
- WifiNanHalMock.callDisabled(HalMockUtils.convertBundleToJson(args).toString());
-
- verify(mNanStateManager).onNanDown(WifiNanSessionListener.FAIL_REASON_OTHER);
- }
-
- /*
- * Utilities
- */
-
- private void testEnable(short transactionId, int clusterLow, int clusterHigh, int masterPref,
- boolean enable5g) throws JSONException {
- ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(clusterLow)
- .setClusterHigh(clusterHigh).setMasterPreference(masterPref)
- .setSupport5gBand(enable5g).build();
-
- mDut.enableAndConfigure(transactionId, configRequest);
-
- verify(mNanHalMock).enableHalMockNative(eq(transactionId), mArgs.capture());
-
- Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
-
- collector.checkThat("master_pref", argsData.getInt("master_pref"), equalTo(masterPref));
- collector.checkThat("cluster_low", argsData.getInt("cluster_low"), equalTo(clusterLow));
- collector.checkThat("cluster_high", argsData.getInt("cluster_high"), equalTo(clusterHigh));
- collector.checkThat("config_support_5g", argsData.getInt("config_support_5g"), equalTo(1));
- collector.checkThat("support_5g_val", argsData.getInt("support_5g_val"),
- equalTo(enable5g ? 1 : 0));
-
- collector.checkThat("config_sid_beacon", argsData.getInt("config_sid_beacon"), equalTo(0));
- collector.checkThat("config_2dot4g_rssi_close", argsData.getInt("config_2dot4g_rssi_close"),
- equalTo(0));
- collector.checkThat("config_2dot4g_rssi_middle",
- argsData.getInt("config_2dot4g_rssi_middle"), equalTo(0));
- collector.checkThat("config_2dot4g_rssi_proximity",
- argsData.getInt("config_2dot4g_rssi_proximity"), equalTo(0));
- collector.checkThat("config_hop_count_limit", argsData.getInt("config_hop_count_limit"),
- equalTo(0));
- collector.checkThat("config_2dot4g_support", argsData.getInt("config_2dot4g_support"),
- equalTo(0));
- collector.checkThat("config_2dot4g_beacons", argsData.getInt("config_2dot4g_beacons"),
- equalTo(0));
- collector.checkThat("config_2dot4g_sdf", argsData.getInt("config_2dot4g_sdf"), equalTo(0));
- collector.checkThat("config_5g_beacons", argsData.getInt("config_5g_beacons"), equalTo(0));
- collector.checkThat("config_5g_sdf", argsData.getInt("config_5g_sdf"), equalTo(0));
- collector.checkThat("config_5g_rssi_close", argsData.getInt("config_5g_rssi_close"),
- equalTo(0));
- collector.checkThat("config_5g_rssi_middle", argsData.getInt("config_5g_rssi_middle"),
- equalTo(0));
- collector.checkThat("config_5g_rssi_close_proximity",
- argsData.getInt("config_5g_rssi_close_proximity"), equalTo(0));
- collector.checkThat("config_rssi_window_size", argsData.getInt("config_rssi_window_size"),
- equalTo(0));
- collector.checkThat("config_oui", argsData.getInt("config_oui"), equalTo(0));
- collector.checkThat("config_intf_addr", argsData.getInt("config_intf_addr"), 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("config_hop_count_force", argsData.getInt("config_hop_count_force"),
- 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();
-
- PublishSettings publishSettings = new PublishSettings.Builder().setPublishType(publishType)
- .setPublishCount(publishCount).setTtlSec(publishTtl).build();
-
- mDut.publish(transactionId, publishId, publishData, publishSettings);
-
- verify(mNanHalMock).publishHalMockNative(eq(transactionId), mArgs.capture());
-
- Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
-
- collector.checkThat("publish_id", argsData.getInt("publish_id"), equalTo(publishId));
- 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));
- collector.checkThat("publish_count", argsData.getInt("publish_count"),
- equalTo(publishCount));
- collector.checkThat("service_name_len", argsData.getInt("service_name_len"),
- equalTo(serviceName.length()));
- collector.checkThat("service_name", argsData.getByteArray("service_name"),
- equalTo(serviceName.getBytes()));
- collector.checkThat("service_specific_info_len",
- argsData.getInt("service_specific_info_len"), equalTo(ssi.length()));
- collector.checkThat("service_specific_info", argsData.getByteArray("service_specific_info"),
- equalTo(ssi.getBytes()));
- collector.checkThat("publish_match_indicator", argsData.getInt("publish_match_indicator"),
- equalTo(0));
- collector.checkThat("rx_match_filter_len", argsData.getInt("rx_match_filter_len"),
- equalTo(tlvRx.getActualLength()));
- collector.checkThat("rx_match_filter", argsData.getByteArray("rx_match_filter"),
- equalTo(Arrays.copyOf(tlvRx.getArray(), tlvRx.getActualLength())));
- collector.checkThat("tx_match_filter_len", argsData.getInt("tx_match_filter_len"),
- equalTo(tlvTx.getActualLength()));
- collector.checkThat("tx_match_filter", argsData.getByteArray("tx_match_filter"),
- equalTo(Arrays.copyOf(tlvTx.getArray(), tlvTx.getActualLength())));
- collector.checkThat("rssi_threshold_flag", argsData.getInt("rssi_threshold_flag"),
- equalTo(0));
- collector.checkThat("connmap", argsData.getInt("connmap"), equalTo(0));
- }
-
- 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)
- .setServiceSpecificInfo(ssi)
- .setTxFilter(tlvTx.getArray(), tlvTx.getActualLength())
- .setRxFilter(tlvRx.getArray(), tlvRx.getActualLength()).build();
-
- SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
- .setSubscribeType(subscribeType).setSubscribeCount(subscribeCount)
- .setTtlSec(subscribeTtl).build();
-
- mDut.subscribe(transactionId, subscribeId, subscribeData, subscribeSettings);
-
- verify(mNanHalMock).subscribeHalMockNative(eq(transactionId), mArgs.capture());
-
- Bundle argsData = HalMockUtils.convertJsonToBundle(mArgs.getValue());
-
- collector.checkThat("subscribe_id", argsData.getInt("subscribe_id"), equalTo(subscribeId));
- collector.checkThat("ttl", argsData.getInt("ttl"), equalTo(subscribeTtl));
- collector.checkThat("period", argsData.getInt("period"), equalTo(500));
- collector.checkThat("subscribe_type", argsData.getInt("subscribe_type"),
- equalTo(subscribeType));
- collector.checkThat("serviceResponseFilter", argsData.getInt("serviceResponseFilter"),
- equalTo(1));
- collector.checkThat("serviceResponseInclude", argsData.getInt("serviceResponseInclude"),
- equalTo(1));
- collector.checkThat("useServiceResponseFilter", argsData.getInt("useServiceResponseFilter"),
- equalTo(1));
- collector.checkThat("ssiRequiredForMatchIndication",
- argsData.getInt("ssiRequiredForMatchIndication"), equalTo(0));
- collector.checkThat("subscribe_match_indicator",
- argsData.getInt("subscribe_match_indicator"), equalTo(0));
- collector.checkThat("subscribe_count", argsData.getInt("subscribe_count"),
- equalTo(subscribeCount));
- collector.checkThat("service_name_len", argsData.getInt("service_name_len"),
- equalTo(serviceName.length()));
- collector.checkThat("service_name", argsData.getByteArray("service_name"),
- equalTo(serviceName.getBytes()));
- collector.checkThat("service_specific_info_len",
- argsData.getInt("service_specific_info_len"), equalTo(serviceName.length()));
- collector.checkThat("service_specific_info", argsData.getByteArray("service_specific_info"),
- equalTo(ssi.getBytes()));
- collector.checkThat("rx_match_filter_len", argsData.getInt("rx_match_filter_len"),
- equalTo(tlvRx.getActualLength()));
- collector.checkThat("rx_match_filter", argsData.getByteArray("rx_match_filter"),
- equalTo(Arrays.copyOf(tlvRx.getArray(), tlvRx.getActualLength())));
- collector.checkThat("tx_match_filter_len", argsData.getInt("tx_match_filter_len"),
- equalTo(tlvTx.getActualLength()));
- collector.checkThat("tx_match_filter", argsData.getByteArray("tx_match_filter"),
- equalTo(Arrays.copyOf(tlvTx.getArray(), tlvTx.getActualLength())));
- collector.checkThat("rssi_threshold_flag", argsData.getInt("rssi_threshold_flag"),
- equalTo(0));
- collector.checkThat("connmap", argsData.getInt("connmap"), equalTo(0));
- collector.checkThat("num_intf_addr_present", argsData.getInt("num_intf_addr_present"),
- equalTo(0));
- }
-
- private static void installMockNanStateManager(WifiNanStateManager nanStateManager)
- throws Exception {
- Field field = WifiNanStateManager.class.getDeclaredField("sNanStateManagerSingleton");
- field.setAccessible(true);
- field.set(null, nanStateManager);
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanManagerTest.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanManagerTest.java
deleted file mode 100644
index bd4d43b..0000000
--- a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanManagerTest.java
+++ /dev/null
@@ -1,403 +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.nan;
-
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertEquals;
-
-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.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ErrorCollector;
-import org.junit.rules.ExpectedException;
-
-/**
- * Unit test harness for WifiNanManager class.
- */
-@SmallTest
-public class WifiNanManagerTest {
- @Rule
- public ErrorCollector collector = new ErrorCollector();
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- /*
- * ConfigRequest Tests
- */
-
- @Test
- public void testConfigRequestBuilder() {
- final int clusterHigh = 100;
- final int clusterLow = 5;
- final int masterPreference = 55;
- final boolean supportBand5g = true;
-
- ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
- .setClusterLow(clusterLow).setMasterPreference(masterPreference)
- .setSupport5gBand(supportBand5g).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));
- }
-
- @Test
- public void testConfigRequestBuilderMasterPrefNegative() {
- thrown.expect(IllegalArgumentException.class);
- ConfigRequest.Builder builder = new ConfigRequest.Builder();
- builder.setMasterPreference(-1);
- }
-
- @Test
- public void testConfigRequestBuilderMasterPrefReserved1() {
- thrown.expect(IllegalArgumentException.class);
- new ConfigRequest.Builder().setMasterPreference(1);
- }
-
- @Test
- public void testConfigRequestBuilderMasterPrefReserved255() {
- thrown.expect(IllegalArgumentException.class);
- new ConfigRequest.Builder().setMasterPreference(255);
- }
-
- @Test
- public void testConfigRequestBuilderMasterPrefTooLarge() {
- thrown.expect(IllegalArgumentException.class);
- new ConfigRequest.Builder().setMasterPreference(256);
- }
-
- @Test
- public void testConfigRequestBuilderClusterLowNegative() {
- thrown.expect(IllegalArgumentException.class);
- new ConfigRequest.Builder().setClusterLow(-1);
- }
-
- @Test
- public void testConfigRequestBuilderClusterHighNegative() {
- thrown.expect(IllegalArgumentException.class);
- new ConfigRequest.Builder().setClusterHigh(-1);
- }
-
- @Test
- public void testConfigRequestBuilderClusterLowAboveMax() {
- thrown.expect(IllegalArgumentException.class);
- new ConfigRequest.Builder().setClusterLow(ConfigRequest.CLUSTER_ID_MAX + 1);
- }
-
- @Test
- public void testConfigRequestBuilderClusterHighAboveMax() {
- thrown.expect(IllegalArgumentException.class);
- new ConfigRequest.Builder().setClusterHigh(ConfigRequest.CLUSTER_ID_MAX + 1);
- }
-
- @Test
- public void testConfigRequestBuilderClusterLowLargerThanHigh() {
- thrown.expect(IllegalArgumentException.class);
- ConfigRequest configRequest = new ConfigRequest.Builder().setClusterLow(100)
- .setClusterHigh(5).build();
- }
-
- @Test
- public void testConfigRequestParcel() {
- final int clusterHigh = 189;
- final int clusterLow = 25;
- final int masterPreference = 177;
- final boolean supportBand5g = true;
-
- ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
- .setClusterLow(clusterLow).setMasterPreference(masterPreference)
- .setSupport5gBand(supportBand5g).build();
-
- Parcel parcelW = Parcel.obtain();
- configRequest.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
-
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
- ConfigRequest rereadConfigRequest = ConfigRequest.CREATOR.createFromParcel(parcelR);
-
- assertEquals(configRequest, rereadConfigRequest);
- }
-
- /*
- * SubscribeData Tests
- */
-
- @Test
- public void testSubscribeDataBuilder() {
- 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 subscribeCount = 10;
- final int subscribeTtl = 15;
-
- SubscribeSettings subscribeSetting = new SubscribeSettings.Builder()
- .setSubscribeType(subscribeType).setSubscribeCount(subscribeCount)
- .setTtlSec(subscribeTtl).build();
-
- collector.checkThat("mSubscribeType", subscribeType,
- equalTo(subscribeSetting.mSubscribeType));
- collector.checkThat("mSubscribeCount", subscribeCount,
- equalTo(subscribeSetting.mSubscribeCount));
- collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeSetting.mTtlSec));
- }
-
- @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;
- final int subscribeCount = 10;
- final int subscribeTtl = 15;
-
- SubscribeSettings subscribeSetting = new SubscribeSettings.Builder()
- .setSubscribeType(subscribeType).setSubscribeCount(subscribeCount)
- .setTtlSec(subscribeTtl).build();
-
- Parcel parcelW = Parcel.obtain();
- subscribeSetting.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);
-
- assertEquals(subscribeSetting, rereadSubscribeSettings);
- }
-
- /*
- * PublishData Tests
- */
-
- @Test
- public void testPublishDataBuilder() {
- 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 };
-
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(serviceSpecificInfo).setTxFilter(txFilter, txFilter.length)
- .setRxFilter(rxFilter, rxFilter.length).build();
-
- collector.checkThat("mServiceName", serviceName, equalTo(publishData.mServiceName));
- String mServiceSpecificInfo = new String(publishData.mServiceSpecificInfo, 0,
- publishData.mServiceSpecificInfoLength);
- collector.checkThat("mServiceSpecificInfo",
- utilAreArraysEqual(serviceSpecificInfo.getBytes(), serviceSpecificInfo.length(),
- publishData.mServiceSpecificInfo, publishData.mServiceSpecificInfoLength),
- equalTo(true));
- collector.checkThat("mTxFilter", utilAreArraysEqual(txFilter, txFilter.length,
- publishData.mTxFilter, publishData.mTxFilterLength), equalTo(true));
- collector.checkThat("mRxFilter", utilAreArraysEqual(rxFilter, rxFilter.length,
- publishData.mRxFilter, publishData.mRxFilterLength), equalTo(true));
- }
-
- @Test
- public void testPublishDataParcel() {
- 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 };
-
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(serviceSpecificInfo).setTxFilter(txFilter, txFilter.length)
- .setTxFilter(rxFilter, rxFilter.length).build();
-
- Parcel parcelW = Parcel.obtain();
- publishData.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);
-
- assertEquals(publishData, rereadPublishData);
- }
-
- /*
- * PublishSettings Tests
- */
-
- @Test
- public void testPublishSettingsBuilder() {
- final int publishType = PublishSettings.PUBLISH_TYPE_SOLICITED;
- final int publishCount = 10;
- final int publishTtl = 15;
-
- PublishSettings publishSetting = new PublishSettings.Builder().setPublishType(publishType)
- .setPublishCount(publishCount).setTtlSec(publishTtl).build();
-
- collector.checkThat("mPublishType", publishType, equalTo(publishSetting.mPublishType));
- collector.checkThat("mPublishCount", publishCount, equalTo(publishSetting.mPublishCount));
- collector.checkThat("mTtlSec", publishTtl, equalTo(publishSetting.mTtlSec));
- }
-
- @Test
- public void testPublishSettingsBuilderBadPublishType() {
- thrown.expect(IllegalArgumentException.class);
- new PublishSettings.Builder().setPublishType(5);
- }
-
- @Test
- public void testPublishSettingsBuilderNegativeCount() {
- thrown.expect(IllegalArgumentException.class);
- new PublishSettings.Builder().setPublishCount(-4);
- }
-
- @Test
- public void testPublishSettingsBuilderNegativeTtl() {
- thrown.expect(IllegalArgumentException.class);
- new PublishSettings.Builder().setTtlSec(-10);
- }
-
- @Test
- public void testPublishSettingsParcel() {
- final int publishType = PublishSettings.PUBLISH_TYPE_SOLICITED;
- final int publishCount = 10;
- final int publishTtl = 15;
-
- PublishSettings configSetting = new PublishSettings.Builder().setPublishType(publishType)
- .setPublishCount(publishCount).setTtlSec(publishTtl).build();
-
- Parcel parcelW = Parcel.obtain();
- configSetting.writeToParcel(parcelW, 0);
- byte[] bytes = parcelW.marshall();
- parcelW.recycle();
-
- Parcel parcelR = Parcel.obtain();
- parcelR.unmarshall(bytes, 0, bytes.length);
- parcelR.setDataPosition(0);
- PublishSettings rereadPublishSettings = PublishSettings.CREATOR.createFromParcel(parcelR);
-
- assertEquals(configSetting, rereadPublishSettings);
- }
-
- /*
- * Utilities
- */
-
- private static boolean utilAreArraysEqual(byte[] x, int xLength, byte[] y, int yLength) {
- if (xLength != yLength) {
- return false;
- }
-
- if (x != null && y != null) {
- for (int i = 0; i < xLength; ++i) {
- if (x[i] != y[i]) {
- return false;
- }
- }
- } else if (xLength != 0) {
- return false; // invalid != invalid
- }
-
- return true;
- }
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanStateManagerTest.java b/tests/wifitests/src/com/android/server/wifi/nan/WifiNanStateManagerTest.java
deleted file mode 100644
index 49abd27..0000000
--- a/tests/wifitests/src/com/android/server/wifi/nan/WifiNanStateManagerTest.java
+++ /dev/null
@@ -1,1147 +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.nan;
-
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.hamcrest.core.IsNull.nullValue;
-import static org.junit.Assert.assertTrue;
-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.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-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.test.suitebuilder.annotation.SmallTest;
-import android.util.SparseArray;
-
-import com.android.server.wifi.MockLooper;
-
-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;
-
-/**
- * Unit test harness for WifiNanStateManager.
- */
-@SmallTest
-public class WifiNanStateManagerTest {
- private MockLooper mMockLooper;
- private WifiNanStateManager mDut;
- @Mock private WifiNanNative mMockNative;
-
- @Rule
- public ErrorCollector collector = new ErrorCollector();
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mMockLooper = new MockLooper();
-
- mDut = installNewNanStateManagerAndResetState();
- mDut.start(mMockLooper.getLooper());
-
- installMockWifiNanNative(mMockNative);
- }
-
- @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;
- final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
-
- 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);
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, 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);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest1));
- short transactionId1 = transactionId.getValue();
-
- mDut.requestConfig(uid, configRequest2);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest2));
- short transactionId2 = transactionId.getValue();
-
- mDut.onClusterChange(WifiNanClientState.CLUSTER_CHANGE_EVENT_STARTED, someMac);
- mDut.onConfigCompleted(transactionId1);
- mDut.onConfigFailed(transactionId2, reason);
- mDut.onInterfaceAddressChange(someMac);
- mDut.onNanDown(reason);
- mMockLooper.dispatchAll();
-
- 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);
- }
-
- @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);
-
- 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);
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, mMockNative);
-
- mDut.connect(uid, mockListener, 0);
- mDut.requestConfig(uid, configRequest1);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(), eq(configRequest1));
- short transactionId1 = transactionId.getValue();
-
- mDut.requestConfig(uid, configRequest2);
- 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);
- mMockLooper.dispatchAll();
-
- verifyZeroInteractions(mockListener);
-
- validateInternalTransactionInfoCleanedUp(transactionId1);
- validateInternalTransactionInfoCleanedUp(transactionId2);
- }
-
- @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;
-
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
-
- PublishSettings publishSettings = new PublishSettings.Builder()
- .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
- .setPublishCount(publishCount).build();
-
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, 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);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
-
- mDut.onPublishFail(transactionId.getValue(), reasonFail);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mockListener).onPublishFail(reasonFail);
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
-
- // publish - success/terminate
- mDut.publish(uid, sessionId, publishData, publishSettings);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
-
- mDut.onPublishSuccess(transactionId.getValue(), publishId1);
- mMockLooper.dispatchAll();
-
- mDut.onPublishTerminated(publishId1, reasonTerminate);
- mMockLooper.dispatchAll();
-
- 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);
- }
-
- @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;
- final int publishId = 15;
-
- PublishData publishData = new PublishData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
-
- PublishSettings publishSettings = new PublishSettings.Builder()
- .setPublishType(PublishSettings.PUBLISH_TYPE_UNSOLICITED)
- .setPublishCount(publishCount).build();
-
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, 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);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
-
- mDut.onPublishFail(transactionId.getValue(), reasonFail);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
-
- // publish - success/terminate
- mDut.publish(uid, sessionId, publishData, publishSettings);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).publish(transactionId.capture(), eq(0), eq(publishData),
- eq(publishSettings));
-
- mDut.onPublishSuccess(transactionId.getValue(), publishId);
- mMockLooper.dispatchAll();
-
- mDut.onPublishTerminated(publishId, reasonTerminate);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- verifyNoMoreInteractions(mockListener, mMockNative);
- }
-
- @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;
-
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
-
- SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
- .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
- .setSubscribeCount(subscribeCount).build();
-
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, 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);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- mDut.onSubscribeFail(transactionId.getValue(), reasonFail);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener).onSubscribeFail(reasonFail);
-
- // subscribe - success/terminate
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId1);
- mMockLooper.dispatchAll();
-
- mDut.onSubscribeTerminated(subscribeId1, reasonTerminate);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener).onSubscribeTerminated(reasonTerminate);
-
- // re-subscribe
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId2);
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(subscribeId2),
- eq(subscribeData), eq(subscribeSettings));
- verifyNoMoreInteractions(mockListener, mMockNative);
- }
-
- @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;
- final int subscribeId = 15;
-
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
-
- SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
- .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
- .setSubscribeCount(subscribeCount).build();
-
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, 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);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- mDut.onSubscribeFail(transactionId.getValue(), reasonFail);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
-
- // subscribe - success/terminate
- mDut.subscribe(uid, sessionId, subscribeData, subscribeSettings);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- mDut.onSubscribeSuccess(transactionId.getValue(), subscribeId);
- mMockLooper.dispatchAll();
-
- mDut.onSubscribeTerminated(subscribeId, reasonTerminate);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- verifyNoMoreInteractions(mockListener, mMockNative);
- }
-
- @Test
- public void testMatchAndMessages() 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 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 String peerMsg = "some message from peer";
- final int messageId = 6948;
-
- SubscribeData subscribeData = new SubscribeData.Builder().setServiceName(serviceName)
- .setServiceSpecificInfo(ssi).build();
-
- SubscribeSettings subscribeSettings = new SubscribeSettings.Builder()
- .setSubscribeType(SubscribeSettings.SUBSCRIBE_TYPE_PASSIVE)
- .setSubscribeCount(subscribeCount).build();
-
- IWifiNanSessionListener mockListener = mock(IWifiNanSessionListener.class);
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- InOrder inOrder = inOrder(mockListener, 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);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).subscribe(transactionId.capture(), eq(0), eq(subscribeData),
- eq(subscribeSettings));
-
- 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(),
- 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(),
- peerMsg.length());
-
- mDut.sendMessage(uid, sessionId, requestorId, ssi.getBytes(), ssi.length(), messageId);
- 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);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener).onMessageSendFail(messageId, reasonFail);
-
- mDut.sendMessage(uid, sessionId, requestorId, ssi.getBytes(), ssi.length(), messageId);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).sendMessage(transactionId.capture(), eq(subscribeId),
- eq(requestorId), eq(peerMac), eq(ssi.getBytes()), eq(ssi.length()));
-
- mDut.onMessageSendSuccess(transactionId.getValue());
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener).onMessageSendSuccess(messageId);
-
- verifyNoMoreInteractions(mockListener, mMockNative);
- }
-
- /**
- * Summary: in a single publish session interact with multiple peers
- * (different MAC addresses).
- */
- @Test
- public void testMultipleMessageSources() throws Exception {
- final int uid = 300;
- 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;
- final int peerId2 = 873;
- final byte[] peerMac1 = HexEncoding.decode("000102030405".toCharArray(), false);
- final byte[] peerMac2 = HexEncoding.decode("060708090A0B".toCharArray(), false);
- final String msgFromPeer1 = "hey from 000102...";
- final String msgFromPeer2 = "hey from 0607...";
- final String msgToPeer1 = "hey there 000102...";
- final String msgToPeer2 = "hey there 0506...";
- final int msgToPeerId1 = 546;
- final int msgToPeerId2 = 9654;
- final int reason = WifiNanSessionListener.FAIL_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();
-
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
- IWifiNanSessionListener mockSessionListener = mock(IWifiNanSessionListener.class);
- InOrder inOrder = inOrder(mMockNative, mockListener, mockSessionListener);
-
- 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);
- 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);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionIdConfig);
- validateInternalTransactionInfoCleanedUp(transactionIdPublish);
- inOrder.verify(mockListener).onConfigCompleted(configRequest);
- inOrder.verify(mockSessionListener).onMessageReceived(peerId1, msgFromPeer1.getBytes(),
- msgFromPeer1.length());
- inOrder.verify(mockSessionListener).onMessageReceived(peerId2, msgFromPeer2.getBytes(),
- msgFromPeer2.length());
- 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();
-
- mDut.onMessageSendFail(transactionIdMsg1, reason);
- mDut.onMessageSendSuccess(transactionIdMsg2);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionIdMsg1);
- validateInternalTransactionInfoCleanedUp(transactionIdMsg2);
- inOrder.verify(mockSessionListener).onMessageSendFail(msgToPeerId1, reason);
- inOrder.verify(mockSessionListener).onMessageSendSuccess(msgToPeerId2);
- verifyNoMoreInteractions(mMockNative, mockListener, mockSessionListener);
- }
-
- /**
- * Summary: interact with a peer which changed its identity (MAC address)
- * but which keeps its requestor instance ID. Should be transparent.
- */
- @Test
- public void testMessageWhilePeerChangesIdentity() throws Exception {
- final int uid = 300;
- 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;
- final byte[] peerMacOrig = HexEncoding.decode("000102030405".toCharArray(), false);
- final byte[] peerMacLater = HexEncoding.decode("060708090A0B".toCharArray(), false);
- final String msgFromPeer1 = "hey from 000102...";
- final String msgFromPeer2 = "hey from 0607...";
- final String msgToPeer1 = "hey there 000102...";
- final String msgToPeer2 = "hey there 0506...";
- final int msgToPeerId1 = 546;
- final int msgToPeerId2 = 9654;
- 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();
-
- ArgumentCaptor<Short> transactionId = ArgumentCaptor.forClass(Short.class);
- IWifiNanEventListener mockListener = mock(IWifiNanEventListener.class);
- IWifiNanSessionListener mockSessionListener = mock(IWifiNanSessionListener.class);
- InOrder inOrder = inOrder(mMockNative, mockListener, mockSessionListener);
-
- 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);
- 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, peerId, peerMacOrig, msgFromPeer1.getBytes(),
- msgFromPeer1.length());
- mDut.sendMessage(uid, sessionId, peerId, msgToPeer1.getBytes(), msgToPeer1.length(),
- msgToPeerId1);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionIdConfig);
- validateInternalTransactionInfoCleanedUp(transactionIdPublish);
- inOrder.verify(mockListener).onConfigCompleted(configRequest);
- inOrder.verify(mockSessionListener).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);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionIdMsg);
- inOrder.verify(mockSessionListener).onMessageSendSuccess(msgToPeerId1);
- inOrder.verify(mockSessionListener).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();
-
- mDut.onMessageSendSuccess(transactionIdMsg);
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionIdMsg);
- inOrder.verify(mockSessionListener).onMessageSendSuccess(msgToPeerId2);
-
- verifyNoMoreInteractions(mMockNative, mockListener, mockSessionListener);
- }
-
- @Test
- public void testConfigs() throws Exception {
- final int uid1 = 9999;
- final int clusterLow1 = 5;
- final int clusterHigh1 = 100;
- final int masterPref1 = 111;
- final int uid2 = 1001;
- final boolean support5g2 = true;
- final int clusterLow2 = 7;
- final int clusterHigh2 = 155;
- final int masterPref2 = 0;
- final int uid3 = 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();
-
- ConfigRequest configRequest2 = new ConfigRequest.Builder().setSupport5gBand(support5g2)
- .setClusterLow(clusterLow2).setClusterHigh(clusterHigh2)
- .setMasterPreference(masterPref2).build();
-
- ConfigRequest configRequest3 = new ConfigRequest.Builder().build();
-
- IWifiNanEventListener mockListener1 = mock(IWifiNanEventListener.class);
- IWifiNanEventListener mockListener2 = mock(IWifiNanEventListener.class);
- IWifiNanEventListener mockListener3 = mock(IWifiNanEventListener.class);
-
- InOrder inOrder = inOrder(mMockNative, mockListener1, mockListener2, mockListener3);
-
- mDut.connect(uid1, mockListener1, WifiNanEventListener.LISTEN_CONFIG_COMPLETED);
- mDut.requestConfig(uid1, configRequest1);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mMockNative).enableAndConfigure(transactionId.capture(),
- crCapture.capture());
- collector.checkThat("merge: stage 0", configRequest1, equalTo(crCapture.getValue()));
-
- mDut.onConfigCompleted(transactionId.getValue());
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener1).onConfigCompleted(configRequest1);
-
- mDut.connect(uid2, mockListener2, WifiNanEventListener.LISTEN_CONFIG_COMPLETED);
- mDut.requestConfig(uid2, configRequest2);
- 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());
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener1).onConfigCompleted(crCapture.getValue());
-
- mDut.connect(uid3, mockListener3, WifiNanEventListener.LISTEN_CONFIG_COMPLETED);
- mDut.requestConfig(uid3, configRequest3);
- mMockLooper.dispatchAll();
-
- 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());
- mMockLooper.dispatchAll();
-
- validateInternalTransactionInfoCleanedUp(transactionId.getValue());
- inOrder.verify(mockListener1).onConfigCompleted(crCapture.getValue());
-
- mDut.disconnect(uid2);
- mMockLooper.dispatchAll();
-
- 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);
- }
-
- /**
- * 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 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 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)
- .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);
-
- 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);
- 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();
-
- validateInternalClientInfoCleanedUp(uid);
- validateInternalTransactionInfoCleanedUp(transactionIdPublish);
-
- mDut.onConfigCompleted(transactionIdConfig);
- mDut.onPublishSuccess(transactionIdPublish, publishId);
- mMockLooper.dispatchAll();
-
- mDut.onPublishTerminated(publishId, reason);
- mMockLooper.dispatchAll();
-
- verifyZeroInteractions(mockListener, mockSessionListener);
- }
-
- /**
- * Summary: destroy a session while there are pending transactions. Validate
- * that no callbacks are called and that internal state is cleaned-up.
- */
- @Test
- public void testDestroySessionWithPendingTransactions() throws Exception {
- final int uid = 128;
- 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)
- .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);
-
- 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);
- 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);
- mMockLooper.dispatchAll();
-
- mDut.onPublishTerminated(publishId, reason);
- mDut.destroySession(uid, subscribeSessionId);
- mMockLooper.dispatchAll();
-
- inOrder.verify(mockListener).onConfigCompleted(configRequest);
- verifyZeroInteractions(mockPublishSessionListener);
- verifyNoMoreInteractions(mockSubscribeSessionListener);
- }
-
- @Test
- public void testTransactionIdIncrement() {
- int loopCount = 100;
-
- short prevId = 0;
- for (int i = 0; i < loopCount; ++i) {
- short id = mDut.createNextTransactionId();
- if (i != 0) {
- assertTrue("Transaction ID incrementing", id > prevId);
- }
- prevId = id;
- }
- }
-
- /*
- * Tests of internal state of WifiNanStateManager: 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.
- */
-
- /**
- * 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.
- *
- * @param transactionId The transaction ID whose state should be erased.
- */
- public void validateInternalTransactionInfoCleanedUp(short transactionId) throws Exception {
- Object info = getInternalPendingTransactionInfo(mDut, transactionId);
- collector.checkThat("Transaction record not cleared up for transactionId=" + transactionId,
- info, 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.
- *
- * @param uid The ID of the client which should be deleted.
- */
- public void validateInternalClientInfoCleanedUp(int uid) throws Exception {
- WifiNanClientState client = getInternalClientState(mDut, uid);
- collector.checkThat("Client record not cleared up for uid=" + uid, client, nullValue());
- }
-
- /*
- * Utilities
- */
-
- private static WifiNanStateManager installNewNanStateManagerAndResetState() 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 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 {
- Field field = WifiNanStateManager.class.getDeclaredField("mClients");
- field.setAccessible(true);
- @SuppressWarnings("unchecked")
- SparseArray<WifiNanClientState> clients = (SparseArray<WifiNanClientState>) field.get(dut);
-
- return clients.get(uid);
- }
-}
-
diff --git a/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java b/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java
new file mode 100644
index 0000000..4443bb1
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackTest.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wifi.p2p;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback;
+import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods;
+import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pProvDiscEvent;
+import android.net.wifi.p2p.nsd.WifiP2pServiceResponse;
+
+import com.android.server.wifi.util.NativeUtil;
+
+import org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+
+/**
+ * Unit tests for SupplicantP2pIfaceCallback
+ */
+public class SupplicantP2pIfaceCallbackTest {
+ private static final String TAG = "SupplicantP2pIfaceCallbackTest";
+
+ private String mIface = "test_p2p0";
+ private WifiP2pMonitor mMonitor;
+ private SupplicantP2pIfaceCallback mDut;
+
+ private byte[] mDeviceAddressInvalid1 = { 0x00 };
+ private byte[] mDeviceAddressInvalid2 = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+ private byte[] mDeviceAddress1Bytes = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
+ private String mDeviceAddress1String = "00:11:22:33:44:55";
+ private byte[] mDeviceAddress2Bytes = { 0x01, 0x12, 0x23, 0x34, 0x45, 0x56 };
+ private String mDeviceAddress2String = "01:12:23:34:45:56";
+ private byte[] mDeviceInfoBytes = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
+ private static final byte[] DEVICE_ADDRESS = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
+
+ private class SupplicantP2pIfaceCallbackSpy extends SupplicantP2pIfaceCallback {
+ SupplicantP2pIfaceCallbackSpy(String iface, WifiP2pMonitor monitor) {
+ super(iface, monitor);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mMonitor = mock(WifiP2pMonitor.class);
+ mDut = new SupplicantP2pIfaceCallbackSpy(mIface, mMonitor);
+ }
+
+ /**
+ * Sunny day scenario for onDeviceFound call.
+ */
+ @Test
+ public void testOnDeviceFound_success() throws Exception {
+ byte[] fakePrimaryDeviceTypeBytes = { 0x01, 0x02, 0x03 };
+ String fakePrimaryDeviceTypeString = "010203";
+ String fakeDeviceName = "test device name";
+ short fakeConfigMethods = 0x1234;
+ byte fakeCapabilities = 123;
+ int fakeGroupCapabilities = 456;
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(String iface, WifiP2pDevice device) {
+ // NOTE: mDeviceAddress1Bytes seems to be ignored by
+ // legacy implementation of WifiP2pDevice.
+ assertEquals(iface, mIface);
+ assertEquals(device.deviceName, fakeDeviceName);
+ assertEquals(device.primaryDeviceType, fakePrimaryDeviceTypeString);
+ assertEquals(device.deviceCapability, fakeCapabilities);
+ assertEquals(device.groupCapability, fakeGroupCapabilities);
+ assertEquals(device.wpsConfigMethodsSupported, fakeConfigMethods);
+ assertEquals(device.deviceAddress, mDeviceAddress2String);
+ assertEquals(device.status, WifiP2pDevice.AVAILABLE);
+ }
+ })
+ .when(mMonitor).broadcastP2pDeviceFound(
+ anyString(), any(WifiP2pDevice.class));
+
+ mDut.onDeviceFound(
+ mDeviceAddress1Bytes, mDeviceAddress2Bytes,
+ fakePrimaryDeviceTypeBytes,
+ fakeDeviceName, fakeConfigMethods,
+ fakeCapabilities, fakeGroupCapabilities,
+ mDeviceInfoBytes);
+
+ mDut.onDeviceFound(
+ mDeviceAddress1Bytes, mDeviceAddress2Bytes,
+ fakePrimaryDeviceTypeBytes,
+ fakeDeviceName, fakeConfigMethods,
+ fakeCapabilities, fakeGroupCapabilities,
+ null);
+
+ // Make sure we issued a broadcast each time.
+ verify(mMonitor, times(2)).broadcastP2pDeviceFound(
+ anyString(), any(WifiP2pDevice.class));
+ }
+
+ /**
+ * Failing scenarios for onDeviceFound call.
+ */
+ @Test
+ public void testOnDeviceFound_invalidArguments() throws Exception {
+ byte[] fakePrimaryDeviceTypeBytes = { 0x01, 0x02, 0x03 };
+ String fakePrimaryDeviceTypeString = "010203";
+ String fakeDeviceName = "test device name";
+ short fakeConfigMethods = 0x1234;
+ byte fakeCapabilities = 123;
+ int fakeGroupCapabilities = 456;
+
+ mDut.onDeviceFound(
+ mDeviceAddress2Bytes, null,
+ fakePrimaryDeviceTypeBytes,
+ fakeDeviceName, fakeConfigMethods,
+ fakeCapabilities, fakeGroupCapabilities,
+ mDeviceInfoBytes);
+ verify(mMonitor, never()).broadcastP2pDeviceFound(
+ anyString(), any(WifiP2pDevice.class));
+
+
+ mDut.onDeviceFound(
+ mDeviceAddress1Bytes, mDeviceAddress2Bytes,
+ null,
+ fakeDeviceName, fakeConfigMethods,
+ fakeCapabilities, fakeGroupCapabilities,
+ mDeviceInfoBytes);
+ verify(mMonitor, never()).broadcastP2pDeviceFound(
+ anyString(), any(WifiP2pDevice.class));
+
+
+ mDut.onDeviceFound(
+ mDeviceAddress1Bytes, mDeviceAddress2Bytes,
+ fakePrimaryDeviceTypeBytes,
+ null, fakeConfigMethods,
+ fakeCapabilities, fakeGroupCapabilities,
+ mDeviceInfoBytes);
+ verify(mMonitor, never()).broadcastP2pDeviceFound(
+ anyString(), any(WifiP2pDevice.class));
+
+
+ mDut.onDeviceFound(
+ mDeviceAddress1Bytes, mDeviceAddressInvalid1,
+ fakePrimaryDeviceTypeBytes,
+ null, fakeConfigMethods,
+ fakeCapabilities, fakeGroupCapabilities,
+ mDeviceInfoBytes);
+ verify(mMonitor, never()).broadcastP2pDeviceFound(
+ anyString(), any(WifiP2pDevice.class));
+
+
+ mDut.onDeviceFound(
+ mDeviceAddress1Bytes, mDeviceAddressInvalid2,
+ fakePrimaryDeviceTypeBytes,
+ null, fakeConfigMethods,
+ fakeCapabilities, fakeGroupCapabilities,
+ mDeviceInfoBytes);
+ verify(mMonitor, never()).broadcastP2pDeviceFound(
+ anyString(), any(WifiP2pDevice.class));
+ }
+
+ /**
+ * Sunny day scenario for onDeviceLost call.
+ */
+ @Test
+ public void testOnDeviceLost_success() throws Exception {
+ doAnswer(new AnswerWithArguments() {
+ public void answer(String iface, WifiP2pDevice device) {
+ assertEquals(iface, mIface);
+ assertEquals(device.deviceAddress, mDeviceAddress1String);
+ assertEquals(device.status, WifiP2pDevice.UNAVAILABLE);
+ }
+ })
+ .when(mMonitor).broadcastP2pDeviceLost(
+ anyString(), any(WifiP2pDevice.class));
+
+ mDut.onDeviceLost(mDeviceAddress1Bytes);
+
+ // Make sure we issued a broadcast each time.
+ verify(mMonitor, times(1)).broadcastP2pDeviceLost(
+ anyString(), any(WifiP2pDevice.class));
+ }
+
+ /**
+ * Failing scenarios for onDeviceLost call.
+ */
+ @Test
+ public void testOnDeviceLost_invalidArguments() throws Exception {
+ mDut.onDeviceLost(null);
+ verify(mMonitor, never()).broadcastP2pDeviceLost(
+ anyString(), any(WifiP2pDevice.class));
+
+ mDut.onDeviceLost(mDeviceAddressInvalid1);
+ verify(mMonitor, never()).broadcastP2pDeviceLost(
+ anyString(), any(WifiP2pDevice.class));
+
+ mDut.onDeviceLost(mDeviceAddressInvalid2);
+ verify(mMonitor, never()).broadcastP2pDeviceLost(
+ anyString(), any(WifiP2pDevice.class));
+ }
+
+ /**
+ * Sunny day scenario for onGoNegotiationRequest call.
+ */
+ @Test
+ public void testOnGoNegotiationRequest_success() throws Exception {
+ HashSet<Integer> setups = new HashSet<Integer>();
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(String iface, WifiP2pConfig config) {
+ assertEquals(iface, mIface);
+ assertNotNull(config.wps);
+ setups.add(config.wps.setup);
+ assertEquals(config.deviceAddress, mDeviceAddress1String);
+ }
+ })
+ .when(mMonitor).broadcastP2pGoNegotiationRequest(
+ anyString(), any(WifiP2pConfig.class));
+
+ mDut.onGoNegotiationRequest(mDeviceAddress1Bytes,
+ (short)ISupplicantP2pIfaceCallback.WpsDevPasswordId.USER_SPECIFIED);
+ assertTrue(setups.contains(WpsInfo.DISPLAY));
+
+ mDut.onGoNegotiationRequest(mDeviceAddress1Bytes,
+ (short)ISupplicantP2pIfaceCallback.WpsDevPasswordId.PUSHBUTTON);
+ assertTrue(setups.contains(WpsInfo.PBC));
+
+ mDut.onGoNegotiationRequest(mDeviceAddress1Bytes,
+ (short)ISupplicantP2pIfaceCallback.WpsDevPasswordId.REGISTRAR_SPECIFIED);
+ assertTrue(setups.contains(WpsInfo.KEYPAD));
+
+ // Invalid should default to PBC
+ setups.clear();
+ mDut.onGoNegotiationRequest(mDeviceAddress1Bytes, (short)0xffff);
+ assertTrue(setups.contains(WpsInfo.PBC));
+ }
+
+ /**
+ * Failing scenarios for onGoNegotiationRequest call.
+ */
+ @Test
+ public void testOnGoNegotiationRequest_invalidArguments() throws Exception {
+ mDut.onGoNegotiationRequest(null, (short)0);
+ verify(mMonitor, never()).broadcastP2pDeviceLost(
+ anyString(), any(WifiP2pDevice.class));
+
+ mDut.onGoNegotiationRequest(mDeviceAddressInvalid1, (short)0);
+ verify(mMonitor, never()).broadcastP2pDeviceLost(
+ anyString(), any(WifiP2pDevice.class));
+
+ mDut.onGoNegotiationRequest(mDeviceAddressInvalid2, (short)0);
+ verify(mMonitor, never()).broadcastP2pDeviceLost(
+ anyString(), any(WifiP2pDevice.class));
+ }
+
+ /**
+ * Sunny day scenario for onGroupStarted call.
+ */
+ @Test
+ public void testOnGroupStarted_success() throws Exception {
+ String fakeName = "group name";
+ String fakePassphrase = "secret";
+ ArrayList<Byte> fakeSsidBytesList = new ArrayList<Byte>() {{
+ add((byte)0x30);
+ add((byte)0x31);
+ add((byte)0x32);
+ add((byte)0x33);
+ }};
+ String fakeSsidString = "0123";
+ HashSet<String> passwords = new HashSet<String>();
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(String iface, WifiP2pGroup group) {
+ assertEquals(iface, mIface);
+ assertNotNull(group.getOwner());
+ assertEquals(group.getOwner().deviceAddress, mDeviceAddress1String);
+ assertEquals(group.getNetworkId(), WifiP2pGroup.PERSISTENT_NET_ID);
+ passwords.add(group.getPassphrase());
+ assertEquals(group.getInterface(), fakeName);
+ assertEquals(group.getNetworkName(), fakeSsidString);
+ }
+ })
+ .when(mMonitor).broadcastP2pGroupStarted(
+ anyString(), any(WifiP2pGroup.class));
+
+ mDut.onGroupStarted(
+ fakeName, true, fakeSsidBytesList, 1, null, fakePassphrase,
+ mDeviceAddress1Bytes, true);
+ assertTrue(passwords.contains(fakePassphrase));
+
+ mDut.onGroupStarted(
+ fakeName, true, fakeSsidBytesList, 1, null, null,
+ mDeviceAddress1Bytes, true);
+ assertTrue(passwords.contains(null));
+
+ verify(mMonitor, times(2)).broadcastP2pGroupStarted(
+ anyString(), any(WifiP2pGroup.class));
+ }
+
+ /**
+ * Failing scenarios for onGroupStarted call.
+ */
+ @Test
+ public void testOnGroupStarted_invalidArguments() throws Exception {
+ String fakeName = "group name";
+ String fakePassphrase = "secret";
+ ArrayList<Byte> fakeSsidBytesList = new ArrayList<Byte>() {{
+ add((byte)0x30);
+ add((byte)0x31);
+ add((byte)0x32);
+ add((byte)0x33);
+ }};
+ String fakeSsidString = "0123";
+
+ mDut.onGroupStarted(
+ null, true, fakeSsidBytesList, 1, null, fakePassphrase,
+ mDeviceAddress1Bytes, true);
+ verify(mMonitor, never()).broadcastP2pGroupStarted(
+ anyString(), any(WifiP2pGroup.class));
+
+ mDut.onGroupStarted(
+ fakeName, true, null, 1, null, fakePassphrase,
+ mDeviceAddress1Bytes, true);
+ verify(mMonitor, never()).broadcastP2pGroupStarted(
+ anyString(), any(WifiP2pGroup.class));
+
+ mDut.onGroupStarted(
+ fakeName, true, fakeSsidBytesList, 1, null, fakePassphrase,
+ null, true);
+ verify(mMonitor, never()).broadcastP2pGroupStarted(
+ anyString(), any(WifiP2pGroup.class));
+ }
+
+ /**
+ * Test provision discovery callback.
+ */
+ @Test
+ public void testOnProvisionDiscoveryCompleted() throws Exception {
+ byte[] p2pDeviceAddr = DEVICE_ADDRESS;
+ boolean isRequest = false;
+ byte status = ISupplicantP2pIfaceCallback.P2pProvDiscStatusCode.SUCCESS;
+ short configMethods = WpsConfigMethods.DISPLAY;
+ String generatedPin = "12345678";
+
+ ArgumentCaptor<WifiP2pProvDiscEvent> discEventCaptor =
+ ArgumentCaptor.forClass(WifiP2pProvDiscEvent.class);
+ mDut.onProvisionDiscoveryCompleted(
+ p2pDeviceAddr, isRequest, status, configMethods, generatedPin);
+ verify(mMonitor).broadcastP2pProvisionDiscoveryEnterPin(
+ anyString(), discEventCaptor.capture());
+ assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event);
+
+ configMethods = WpsConfigMethods.KEYPAD;
+ mDut.onProvisionDiscoveryCompleted(
+ p2pDeviceAddr, isRequest, status, configMethods, generatedPin);
+ verify(mMonitor).broadcastP2pProvisionDiscoveryShowPin(
+ anyString(), discEventCaptor.capture());
+ assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event);
+ assertEquals(generatedPin, discEventCaptor.getValue().pin);
+
+ isRequest = true;
+ configMethods = WpsConfigMethods.KEYPAD;
+ mDut.onProvisionDiscoveryCompleted(
+ p2pDeviceAddr, isRequest, status, configMethods, generatedPin);
+ verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryEnterPin(
+ anyString(), discEventCaptor.capture());
+ assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event);
+
+ configMethods = WpsConfigMethods.DISPLAY;
+ mDut.onProvisionDiscoveryCompleted(
+ p2pDeviceAddr, isRequest, status, configMethods, generatedPin);
+ verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryShowPin(
+ anyString(), discEventCaptor.capture());
+ assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event);
+ assertEquals(generatedPin, discEventCaptor.getValue().pin);
+
+ isRequest = false;
+ configMethods = WpsConfigMethods.PUSHBUTTON;
+ mDut.onProvisionDiscoveryCompleted(
+ p2pDeviceAddr, isRequest, status, configMethods, generatedPin);
+ verify(mMonitor).broadcastP2pProvisionDiscoveryPbcResponse(
+ anyString(), discEventCaptor.capture());
+ assertEquals(WifiP2pProvDiscEvent.PBC_RSP, discEventCaptor.getValue().event);
+
+ isRequest = true;
+ mDut.onProvisionDiscoveryCompleted(
+ p2pDeviceAddr, isRequest, status, configMethods, generatedPin);
+ verify(mMonitor).broadcastP2pProvisionDiscoveryPbcRequest(
+ anyString(), discEventCaptor.capture());
+ assertEquals(WifiP2pProvDiscEvent.PBC_REQ, discEventCaptor.getValue().event);
+ }
+
+ /**
+ * Test staAuth with device address, should trigger ApStaConnected broadcast
+ */
+ @Test
+ public void testStaAuth_success() {
+ // Trigger onStaAuthorized callback, ensure wifimonitor broadcast is sent with WifiP2pDevice
+ // using the p2pDeviceAddress
+ ArgumentCaptor<WifiP2pDevice> p2pDeviceCaptor =
+ ArgumentCaptor.forClass(WifiP2pDevice.class);
+ mDut.onStaAuthorized(mDeviceAddress1Bytes, mDeviceAddress2Bytes);
+ verify(mMonitor).broadcastP2pApStaConnected(any(String.class), p2pDeviceCaptor.capture());
+ assertEquals(mDeviceAddress2String, p2pDeviceCaptor.getValue().deviceAddress);
+ }
+
+ /**
+ * Test staAuth without device address, should trigger ApStaConnected broadcast using srcAddress
+ */
+ @Test
+ public void testStaAuth_noDeviceAddress_success() {
+ // Trigger onStaAuthorized callback, using a zero'd p2pDeviceAddress, ensure wifimonitor
+ // broadcast is sent with WifiP2pDevice using the srcAddress
+ ArgumentCaptor<WifiP2pDevice> p2pDeviceCaptor =
+ ArgumentCaptor.forClass(WifiP2pDevice.class);
+ mDut.onStaAuthorized(mDeviceAddress1Bytes, NativeUtil.ANY_MAC_BYTES);
+ verify(mMonitor).broadcastP2pApStaConnected(any(String.class), p2pDeviceCaptor.capture());
+ assertEquals(mDeviceAddress1String, p2pDeviceCaptor.getValue().deviceAddress);
+ }
+
+ // TLVS hex data encoded as a hex string.
+ // Taken directly from an observed supplicant service response event
+ private static final String SERV_DISC_RESP_TLVS = "1d00010100076578616d706c650b5f6166706f766572"
+ + "746370c00c001001001e000101000b5f6166706f766572746370c00c000c01074578616d706c65c0273c"
+ + "00010100096d797072696e746572045f697070c00c00100109747874766572733d311a70646c3d617070"
+ + "6c69636174696f6e2f706f73747363726970741900010100045f697070c00c000c01094d795072696e74"
+ + "6572c0275f000201000a757569643a36383539646564652d383537342d353961622d393333322d313233"
+ + "3435363738393031313a3a75726e3a736368656d61732d75706e702d6f72673a736572766963653a436f"
+ + "6e6e656374696f6e4d616e616765723a3159000201000a757569643a36383539646564652d383537342d"
+ + "353961622d393333322d3132333435363738393031313a3a75726e3a736368656d61732d75706e702d6f"
+ + "72673a736572766963653a41565472616e73706f72743a315a000201000a757569643a36383539646564"
+ + "652d383537342d353961622d393333322d3132333435363738393031313a3a75726e3a736368656d6173"
+ + "2d75706e702d6f72673a6465766963653a4d6564696152656e64657265723a313e000201000a75756964"
+ + "3a36383539646564652d383537342d353961622d393333322d3132333435363738393031313a3a75706e"
+ + "703a726f6f746465766963652d000201000a757569643a36383539646564652d383537342d353961622d"
+ + "393333322d313233343536373839303131";
+
+ /**
+ * Pretty basic onServiceDiscoveryResponse callback test.
+ * Mocks the callback event, passing some observed real data to it, and ensures that it returns
+ * a non-null WifiP2pServiceResponse list.
+ */
+ @Test
+ public void testOnServiceDiscoveryResponseCompleted_success() throws Exception {
+ ArrayList<Byte> tlvs = NativeUtil.byteArrayToArrayList(hexStr2Bin(SERV_DISC_RESP_TLVS));
+ ArgumentCaptor<List<WifiP2pServiceResponse>> respListCaptor =
+ ArgumentCaptor.forClass(List.class);
+ mDut.onServiceDiscoveryResponse(
+ mDeviceAddress1Bytes,
+ (short) 10 /* unused updateIndicator value */,
+ tlvs);
+ verify(mMonitor).broadcastP2pServiceDiscoveryResponse(anyString(),
+ respListCaptor.capture());
+ assertNotNull(respListCaptor.getValue());
+ }
+
+ /**
+ * Converts hex string to byte array.
+ *
+ * @param hex hex string. if invalid, return null.
+ * @return binary data.
+ */
+ private static byte[] hexStr2Bin(String hex) {
+ int sz = hex.length() / 2;
+ byte[] b = new byte[hex.length() / 2];
+ for (int i = 0; i < sz; i++) {
+ try {
+ b[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ return b;
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java b/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java
new file mode 100644
index 0000000..bcce0ac
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java
@@ -0,0 +1,2618 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wifi.p2p;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
+import android.hardware.wifi.supplicant.V1_0.ISupplicant;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantIface;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIface;
+import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pNetwork;
+import android.hardware.wifi.supplicant.V1_0.IfaceType;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatus;
+import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.net.wifi.WpsInfo;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pGroupList;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.text.TextUtils;
+
+import com.android.server.wifi.util.NativeUtil;
+
+import org.junit.Assert.*;
+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.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Unit tests for SupplicantP2pIfaceHal
+ */
+public class SupplicantP2pIfaceHalTest {
+ private static final String TAG = "SupplicantP2pIfaceHalTest";
+ private SupplicantP2pIfaceHal mDut;
+ @Mock IServiceManager mServiceManagerMock;
+ @Mock ISupplicant mISupplicantMock;
+ @Mock ISupplicantIface mISupplicantIfaceMock;
+ @Mock ISupplicantP2pIface mISupplicantP2pIfaceMock;
+ @Mock ISupplicantP2pNetwork mISupplicantP2pNetworkMock;
+
+ SupplicantStatus mStatusSuccess;
+ SupplicantStatus mStatusFailure;
+ RemoteException mRemoteException;
+ ISupplicant.IfaceInfo mStaIface;
+ ISupplicant.IfaceInfo mP2pIface;
+ ArrayList<ISupplicant.IfaceInfo> mIfaceInfoList;
+
+ final String mIfaceName = "virtual_interface_name";
+ final String mSsid = "\"SSID\"";
+ final ArrayList<Byte> mSsidBytes = new ArrayList<Byte>() {{
+ add((byte)'S'); add((byte)'S'); add((byte)'I'); add((byte)'D');
+ }};
+ final String mPeerMacAddress = "00:11:22:33:44:55";
+ final byte mPeerMacAddressBytes[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
+ final String mGroupOwnerMacAddress = "01:12:23:34:45:56";
+ final byte mGroupOwnerMacAddressBytes[] = { 0x01, 0x12, 0x23, 0x34, 0x45, 0x56 };
+ final String mInvalidMacAddress1 = "00:11:22:33:44";
+ final String mInvalidMacAddress2 = ":::::";
+ final String mInvalidMacAddress3 = "invalid";
+ final byte mInvalidMacAddressBytes1[] = null;
+ final byte mInvalidMacAddressBytes2[] = {};
+ final byte mInvalidMacAddressBytes3[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
+ final byte mInvalidMacAddressBytes4[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+ HashSet<String> mInvalidMacAddresses = new HashSet<String>(Arrays.asList(
+ mInvalidMacAddress1, mInvalidMacAddress2,
+ mInvalidMacAddress3));
+ HashSet<byte[]> mInvalidMacAddressesBytes = new HashSet<byte[]>(Arrays.asList(
+ mInvalidMacAddressBytes1, mInvalidMacAddressBytes2,
+ mInvalidMacAddressBytes3, mInvalidMacAddressBytes4));
+
+ final String mInvalidService1 = null;
+ final String mInvalidService2 = "service";
+ final String mValidServiceRequestString = "30313233";
+ final byte[] mValidServiceRequestBytes = { 0x30, 0x31, 0x32, 0x33 };
+ final String mInvalidServiceRequestString = "not a hex string";
+ final String mInvalidUpnpService1 = "upnp";
+ final String mInvalidUpnpService2 = "upnp 1";
+ final String mInvalidUpnpService3 = "upnp invalid_number name";
+ final String mInvalidBonjourService1 = "bonjour";
+ final String mInvalidBonjourService2 = "bonjour 123456";
+ final String mInvalidBonjourService3 = "bonjour invalid_hex 123456";
+ final String mInvalidBonjourService4 = "bonjour 123456 invalid_hex";
+ final String mValidUpnpService = "upnp 10 serviceName";
+ final int mValidUpnpServiceVersion = 16;
+ final String mValidUpnpServiceName = "serviceName";
+ final String mValidBonjourService = "bonjour 30313233 34353637";
+ final ArrayList<Byte> mValidBonjourServiceRequest = new ArrayList<Byte>() {{
+ add((byte)'0'); add((byte)'1'); add((byte)'2'); add((byte)'3');
+ }};
+ final ArrayList<Byte> mValidBonjourServiceResponse = new ArrayList<Byte>() {{
+ add((byte)'4'); add((byte)'5'); add((byte)'6'); add((byte)'7');
+ }};
+
+
+ private ArgumentCaptor<IHwBinder.DeathRecipient> mDeathRecipientCaptor =
+ ArgumentCaptor.forClass(IHwBinder.DeathRecipient.class);
+ private ArgumentCaptor<IServiceNotification.Stub> mServiceNotificationCaptor =
+ ArgumentCaptor.forClass(IServiceNotification.Stub.class);
+ private InOrder mInOrder;
+
+ private class SupplicantP2pIfaceHalSpy extends SupplicantP2pIfaceHal {
+ SupplicantP2pIfaceHalSpy() {
+ super(null);
+ }
+
+ @Override
+ protected IServiceManager getServiceManagerMockable() throws RemoteException {
+ return mServiceManagerMock;
+ }
+
+ @Override
+ protected ISupplicant getSupplicantMockable() throws RemoteException {
+ return mISupplicantMock;
+ }
+
+ @Override
+ protected ISupplicantP2pIface getP2pIfaceMockable(ISupplicantIface iface) {
+ return mISupplicantP2pIfaceMock;
+ }
+
+ @Override
+ protected ISupplicantP2pNetwork getP2pNetworkMockable(ISupplicantNetwork network) {
+ return mISupplicantP2pNetworkMock;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mStatusSuccess = createSupplicantStatus(SupplicantStatusCode.SUCCESS);
+ mStatusFailure = createSupplicantStatus(SupplicantStatusCode.FAILURE_UNKNOWN);
+ mRemoteException = new RemoteException("Test Remote Exception");
+ mStaIface = createIfaceInfo(IfaceType.STA, "wlan0");
+ mP2pIface = createIfaceInfo(IfaceType.P2P, "p2p0");
+
+ mIfaceInfoList = new ArrayList<ISupplicant.IfaceInfo>();
+ mIfaceInfoList.add(mStaIface);
+ mIfaceInfoList.add(mP2pIface);
+
+ when(mServiceManagerMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong())).thenReturn(true);
+ when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
+ any(IServiceNotification.Stub.class))).thenReturn(true);
+ when(mISupplicantMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong())).thenReturn(true);
+ when(mISupplicantP2pIfaceMock.linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong())).thenReturn(true);
+ mDut = new SupplicantP2pIfaceHalSpy();
+ }
+
+ /**
+ * Sunny day scenario for SupplicantP2pIfaceHal initialization
+ * Asserts successful initialization
+ */
+ @Test
+ public void testInitialize_success() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ }
+
+ /**
+ * Tests the initialization flow, with a RemoteException occurring when 'getInterface' is called
+ * Ensures initialization fails.
+ */
+ @Test
+ public void testInitialize_remoteExceptionFailure() throws Exception {
+ executeAndValidateInitializationSequence(true, false, false);
+ }
+
+ /**
+ * Tests the initialization flow, with listInterfaces returning 0 interfaces.
+ * Ensures failure
+ */
+ @Test
+ public void testInitialize_zeroInterfacesFailure() throws Exception {
+ executeAndValidateInitializationSequence(false, true, false);
+ }
+
+ /**
+ * Tests the initialization flow, with a null interface being returned by getInterface.
+ * Ensures initialization fails.
+ */
+ @Test
+ public void testInitialize_nullInterfaceFailure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, true);
+ }
+
+ /**
+ * Sunny day scenario for getName()
+ */
+ @Test
+ public void testGetName_success() throws Exception {
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantIface.getNameCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, mIfaceName);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getName(any(ISupplicantIface.getNameCallback.class));
+
+ // Default value when service is not initialized.
+ assertNull(mDut.getName());
+ executeAndValidateInitializationSequence(false, false, false);
+ assertEquals(mIfaceName, mDut.getName());
+ }
+
+ /**
+ * Verify that getName returns null, if status is not SUCCESS.
+ */
+ @Test
+ public void testGetName_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantIface.getNameCallback cb) throws RemoteException {
+ cb.onValues(mStatusFailure, "none");
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getName(any(ISupplicantIface.getNameCallback.class));
+ assertNull(mDut.getName());
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that getName disconnects and returns null, if HAL throws exception.
+ */
+ @Test
+ public void testGetName_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantIface.getNameCallback cb) throws RemoteException {
+ throw new RemoteException("Test");
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getName(any(ISupplicantIface.getNameCallback.class));
+ assertNull(mDut.getName());
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for find()
+ */
+ @Test
+ public void testFind_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.find(anyInt())).thenReturn(mStatusSuccess);
+ // Default value when service is not yet initialized.
+ assertFalse(mDut.find(1));
+
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.find(1));
+ assertFalse(mDut.find(-1));
+ }
+
+ /**
+ * Verify that find returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testFind_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.find(anyInt())).thenReturn(mStatusFailure);
+ assertFalse(mDut.find(1));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that find disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testFind_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.find(anyInt())).thenThrow(mRemoteException);
+ assertFalse(mDut.find(0));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for stopFind()
+ */
+ @Test
+ public void testStopFind_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.stopFind()).thenReturn(mStatusSuccess);
+ // Default value when service is not yet initialized.
+ assertFalse(mDut.stopFind());
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.stopFind());
+ }
+
+ /**
+ * Verify that stopFind returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testStopFind_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.stopFind()).thenReturn(mStatusFailure);
+ assertFalse(mDut.stopFind());
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that stopFind disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testStopFind_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.stopFind()).thenThrow(mRemoteException);
+ assertFalse(mDut.stopFind());
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for flush()
+ */
+ @Test
+ public void testFlush_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.flush()).thenReturn(mStatusSuccess);
+ // Default value when service is not yet initialized.
+ assertFalse(mDut.flush());
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.flush());
+ }
+
+ /**
+ * Verify that flush returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testFlush_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.flush()).thenReturn(mStatusFailure);
+ assertFalse(mDut.flush());
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that flush disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testFlush_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.flush()).thenThrow(mRemoteException);
+ assertFalse(mDut.flush());
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for serviceFlush()
+ */
+ @Test
+ public void testServiceFlush_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.flushServices()).thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.serviceFlush());
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.serviceFlush());
+ }
+
+ /**
+ * Verify that serviceFlush returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testServiceFlush_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.flushServices()).thenReturn(mStatusFailure);
+ assertFalse(mDut.serviceFlush());
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that serviceFlush disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testServiceFlush_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.flushServices()).thenThrow(mRemoteException);
+ assertFalse(mDut.serviceFlush());
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for setPowerSave()
+ */
+ @Test
+ public void testSetPowerSave_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.setPowerSave(eq(mIfaceName), anyBoolean()))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.setPowerSave(mIfaceName, true));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.setPowerSave(mIfaceName, true));
+ }
+
+ /**
+ * Verify that setPowerSave returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testSetPowerSave_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setPowerSave(eq(mIfaceName), anyBoolean()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.setPowerSave(mIfaceName, true));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that setPowerSave disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testSetPowerSave_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setPowerSave(eq(mIfaceName), anyBoolean()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.setPowerSave(mIfaceName, true));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for setGroupIdle()
+ */
+ @Test
+ public void testSetGroupIdle_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.setGroupIdle(eq(mIfaceName), anyInt()))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.setGroupIdle(mIfaceName, 1));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.setGroupIdle(mIfaceName, 1));
+ assertFalse(mDut.setGroupIdle(mIfaceName, -1));
+ }
+
+ /**
+ * Verify that setGroupIdle returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testSetGroupIdle_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setGroupIdle(eq(mIfaceName), anyInt()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.setGroupIdle(mIfaceName, 1));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that setGroupIdle disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testSetGroupIdle_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setGroupIdle(eq(mIfaceName), anyInt()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.setGroupIdle(mIfaceName, 1));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for setSsidPostfix()
+ */
+ @Test
+ public void testSetSsidPostfix_success() throws Exception {
+ String ssid = "SSID POSTFIX";
+ when(mISupplicantP2pIfaceMock.setSsidPostfix(eq(NativeUtil.decodeSsid("\"" + ssid + "\""))))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.setSsidPostfix(ssid));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.setSsidPostfix(ssid));
+ assertFalse(mDut.setSsidPostfix(null));
+ }
+
+ /**
+ * Verify that setSsidPostfix returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testSetSsidPostfix_failure() throws Exception {
+ String ssid = "SSID POSTFIX";
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setSsidPostfix(eq(NativeUtil.decodeSsid("\"" + ssid + "\""))))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.setSsidPostfix(ssid));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that setSsidPostfix disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testSetSsidPostfix_exception() throws Exception {
+ String ssid = "SSID POSTFIX";
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setSsidPostfix(eq(NativeUtil.decodeSsid("\"" + ssid + "\""))))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.setSsidPostfix(ssid));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for connect()
+ */
+ @Test
+ public void testConnect_success() throws Exception {
+ final String configPin = "12345";
+ final HashSet<Integer> methods = new HashSet<>();
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] peer, int method, String pin, boolean joinExisting,
+ boolean persistent, int goIntent,
+ ISupplicantP2pIface.connectCallback cb) throws RemoteException {
+ methods.add(method);
+
+ if (method == ISupplicantP2pIface.WpsProvisionMethod.DISPLAY
+ && TextUtils.isEmpty(pin)) {
+ // Return the configPin for DISPLAY method if the pin was not provided.
+ cb.onValues(mStatusSuccess, configPin);
+ } else {
+ if (method != ISupplicantP2pIface.WpsProvisionMethod.PBC) {
+ // PIN is only required for PIN methods.
+ assertEquals(pin, configPin);
+ }
+ // For all the other cases, there is no generated pin.
+ cb.onValues(mStatusSuccess, "");
+ }
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).connect(
+ eq(mPeerMacAddressBytes), anyInt(), anyString(), anyBoolean(), anyBoolean(),
+ anyInt(), any(ISupplicantP2pIface.connectCallback.class));
+
+ WifiP2pConfig config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.DISPLAY, "");
+
+ // Default value when service is not initialized.
+ assertNull(mDut.connect(config, false));
+
+ executeAndValidateInitializationSequence(false, false, false);
+
+ assertEquals(configPin, mDut.connect(config, false));
+ assertTrue(methods.contains(ISupplicantP2pIface.WpsProvisionMethod.DISPLAY));
+ methods.clear();
+
+ config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.DISPLAY, configPin);
+ assertTrue(mDut.connect(config, false).isEmpty());
+ assertTrue(methods.contains(ISupplicantP2pIface.WpsProvisionMethod.DISPLAY));
+ methods.clear();
+
+ config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.PBC, "");
+ assertTrue(mDut.connect(config, false).isEmpty());
+ assertTrue(methods.contains(ISupplicantP2pIface.WpsProvisionMethod.PBC));
+ methods.clear();
+
+ config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.KEYPAD, configPin);
+ assertTrue(mDut.connect(config, false).isEmpty());
+ assertTrue(methods.contains(ISupplicantP2pIface.WpsProvisionMethod.KEYPAD));
+ }
+
+ /**
+ * Test connect with invalid arguments.
+ */
+ @Test
+ public void testConnect_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] peer, int method, String pin, boolean joinExisting,
+ boolean persistent, int goIntent,
+ ISupplicantP2pIface.connectCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, pin);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).connect(
+ any(byte[].class), anyInt(), anyString(), anyBoolean(), anyBoolean(),
+ anyInt(), any(ISupplicantP2pIface.connectCallback.class));
+
+ WifiP2pConfig config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.DISPLAY, "");
+
+ // unsupported.
+ config.wps.setup = -1;
+ assertNull(mDut.connect(config, false));
+
+ // Invalid peer address.
+ config.wps.setup = WpsInfo.DISPLAY;
+ for (String address : mInvalidMacAddresses) {
+ config.deviceAddress = address;
+ assertNull(mDut.connect(config, false));
+ }
+
+ // null pin not valid.
+ config.wps.setup = WpsInfo.DISPLAY;
+ config.wps.pin = null;
+ assertNull(mDut.connect(config, false));
+
+ // Pin should be empty for PBC.
+ config.wps.setup = WpsInfo.PBC;
+ config.wps.pin = "03455323";
+ assertNull(mDut.connect(config, false));
+ }
+
+ /**
+ * Verify that connect returns null, if status is not SUCCESS.
+ */
+ @Test
+ public void testConnect_failure() throws Exception {
+ final String configPin = "12345";
+ WifiP2pConfig config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.DISPLAY, configPin);
+
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] peer, int method, String pin, boolean joinExisting,
+ boolean persistent, int goIntent,
+ ISupplicantP2pIface.connectCallback cb) throws RemoteException {
+ cb.onValues(mStatusFailure, null);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).connect(
+ eq(mPeerMacAddressBytes), anyInt(), anyString(), anyBoolean(), anyBoolean(),
+ anyInt(), any(ISupplicantP2pIface.connectCallback.class));
+
+ assertNull(mDut.connect(config, false));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that connect disconnects and returns null, if HAL throws exception.
+ */
+ @Test
+ public void testConnect_exception() throws Exception {
+ final String configPin = "12345";
+ WifiP2pConfig config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.DISPLAY, configPin);
+
+ doThrow(mRemoteException)
+ .when(mISupplicantP2pIfaceMock).connect(
+ eq(mPeerMacAddressBytes), anyInt(), anyString(), anyBoolean(), anyBoolean(),
+ anyInt(), any(ISupplicantP2pIface.connectCallback.class));
+
+ assertNull(mDut.connect(config, false));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for cancelConnect()
+ */
+ @Test
+ public void testCancelConnect_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.cancelConnect())
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.cancelConnect());
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.cancelConnect());
+ }
+
+ /**
+ * Verify that cancelConnect returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testCancelConnect_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.cancelConnect())
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.cancelConnect());
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that cancelConnect disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testCancelConnect_exception() throws Exception {
+ String ssid = "\"SSID POSTFIX\"";
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.cancelConnect())
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.cancelConnect());
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for provisionDiscovery()
+ */
+ @Test
+ public void testProvisionDiscovery_success() throws Exception {
+ WifiP2pConfig config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.PBC, "");
+
+ when(mISupplicantP2pIfaceMock.provisionDiscovery(
+ eq(mPeerMacAddressBytes), anyInt()))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.provisionDiscovery(config));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.provisionDiscovery(config));
+ }
+
+ /**
+ * Test provisionDiscovery with invalid arguments.
+ */
+ @Test
+ public void testProvisionDiscovery_invalidArguments() throws Exception {
+ when(mISupplicantP2pIfaceMock.provisionDiscovery(
+ eq(mPeerMacAddressBytes), anyInt()))
+ .thenReturn(mStatusSuccess);
+ executeAndValidateInitializationSequence(false, false, false);
+
+ WifiP2pConfig config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.PBC, "");
+
+ // Unsupported method.
+ config.wps.setup = -1;
+ assertFalse(mDut.provisionDiscovery(config));
+
+ config.wps.setup = WpsInfo.PBC;
+ for (String address : mInvalidMacAddresses) {
+ config.deviceAddress = address;
+ assertFalse(mDut.provisionDiscovery(config));
+ }
+ }
+
+ /**
+ * Verify that provisionDiscovery returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testProvisionDiscovery_failure() throws Exception {
+ WifiP2pConfig config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.PBC, "");
+
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.provisionDiscovery(
+ eq(mPeerMacAddressBytes), anyInt()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.provisionDiscovery(config));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that provisionDiscovery disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testProvisionDiscovery_exception() throws Exception {
+ WifiP2pConfig config = createDummyP2pConfig(mPeerMacAddress, WpsInfo.PBC, "");
+
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.provisionDiscovery(
+ eq(mPeerMacAddressBytes), anyInt()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.provisionDiscovery(config));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for invite()
+ */
+ @Test
+ public void testInvite_success() throws Exception {
+ WifiP2pGroup group = createDummyP2pGroup();
+
+ when(mISupplicantP2pIfaceMock.invite(
+ eq(mIfaceName), eq(mGroupOwnerMacAddressBytes), eq(mPeerMacAddressBytes)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.invite(group, mPeerMacAddress));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.invite(group, mPeerMacAddress));
+ }
+
+ /**
+ * Invite with invalid arguments.
+ */
+ @Test
+ public void testInvite_invalidArguments() throws Exception {
+ WifiP2pGroup group = createDummyP2pGroup();
+
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.invite(
+ anyString(), any(byte[].class), any(byte[].class)))
+ .thenReturn(mStatusSuccess);
+
+ for (String address : mInvalidMacAddresses) {
+ assertFalse(mDut.invite(group, address));
+ }
+
+ for (String address : mInvalidMacAddresses) {
+ group.getOwner().deviceAddress = address;
+ assertFalse(mDut.invite(group, mPeerMacAddress));
+ }
+
+ group.setOwner(null);
+ assertFalse(mDut.invite(group, mPeerMacAddress));
+ assertFalse(mDut.invite(null, mPeerMacAddress));
+ }
+
+ /**
+ * Verify that invite returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testInvite_failure() throws Exception {
+ WifiP2pGroup group = createDummyP2pGroup();
+
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.invite(
+ anyString(), any(byte[].class), any(byte[].class)))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.invite(group, mPeerMacAddress));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that invite disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testInvite_exception() throws Exception {
+ WifiP2pGroup group = createDummyP2pGroup();
+
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.invite(
+ anyString(), any(byte[].class), any(byte[].class)))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.invite(group, mPeerMacAddress));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for reject()
+ */
+ @Test
+ public void testReject_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.reject(eq(mPeerMacAddressBytes)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.reject(mPeerMacAddress));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.reject(mPeerMacAddress));
+ }
+
+ /**
+ * Reject with invalid arguments.
+ */
+ @Test
+ public void testReject_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.reject(any(byte[].class)))
+ .thenReturn(mStatusSuccess);
+
+ for (String address : mInvalidMacAddresses) {
+ assertFalse(mDut.reject(address));
+ }
+ }
+
+ /**
+ * Verify that reject returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testReject_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.reject(any(byte[].class)))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.reject(mPeerMacAddress));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that reject disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testReject_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.reject(any(byte[].class)))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.reject(mPeerMacAddress));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for getDeviceAddress()
+ */
+ @Test
+ public void testGetDeviceAddress_success() throws Exception {
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pIface.getDeviceAddressCallback cb) {
+ cb.onValues(mStatusSuccess, mPeerMacAddressBytes);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getDeviceAddress(
+ any(ISupplicantP2pIface.getDeviceAddressCallback.class));
+
+ // Default value when service is not initialized.
+ assertNull(mDut.getDeviceAddress());
+ executeAndValidateInitializationSequence(false, false, false);
+ assertEquals(mPeerMacAddress, mDut.getDeviceAddress());
+ }
+
+ /**
+ * Test getDeviceAddress() when invalid mac address is being reported.
+ */
+ @Test
+ public void testGetDeviceAddress_invalidResult() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ HashSet<byte[]> addresses = new HashSet<byte[]>(Arrays.asList(
+ mInvalidMacAddressBytes1, mInvalidMacAddressBytes2,
+ mInvalidMacAddressBytes3, mInvalidMacAddressBytes4));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pIface.getDeviceAddressCallback cb) {
+ byte[] address = addresses.iterator().next();
+ cb.onValues(mStatusSuccess, address);
+ addresses.remove(address);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getDeviceAddress(
+ any(ISupplicantP2pIface.getDeviceAddressCallback.class));
+
+ // Default value when service is not initialized.
+ while (!addresses.isEmpty()) {
+ assertNull(mDut.getDeviceAddress());
+ }
+ }
+
+ /**
+ * Verify that getDeviceAddress returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testGetDeviceAddress_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pIface.getDeviceAddressCallback cb) {
+ cb.onValues(mStatusFailure, null);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getDeviceAddress(
+ any(ISupplicantP2pIface.getDeviceAddressCallback.class));
+
+ assertNull(mDut.getDeviceAddress());
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that getDeviceAddress disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testGetDeviceAddress_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doThrow(mRemoteException).when(mISupplicantP2pIfaceMock).getDeviceAddress(
+ any(ISupplicantP2pIface.getDeviceAddressCallback.class));
+
+ assertNull(mDut.getDeviceAddress());
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for getSsid()
+ */
+ @Test
+ public void testGetSsid_success() throws Exception {
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] address, ISupplicantP2pIface.getSsidCallback cb) {
+ cb.onValues(mStatusSuccess, mSsidBytes);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getSsid(
+ eq(mPeerMacAddressBytes),
+ any(ISupplicantP2pIface.getSsidCallback.class));
+
+ // Default value when service is not initialized.
+ assertNull(mDut.getSsid(mPeerMacAddress));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertEquals(mSsid, mDut.getSsid(mPeerMacAddress));
+ }
+
+ /**
+ * Test getSsid() with invalid argument and response.
+ */
+ @Test
+ public void testGetSsid_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] address, ISupplicantP2pIface.getSsidCallback cb) {
+ cb.onValues(mStatusSuccess, mSsidBytes);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getSsid(
+ any(byte[].class), any(ISupplicantP2pIface.getSsidCallback.class));
+
+ for (String address : mInvalidMacAddresses) {
+ assertNull(mDut.getSsid(address));
+ }
+
+ // Simulate null response from HAL.
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] address, ISupplicantP2pIface.getSsidCallback cb) {
+ cb.onValues(mStatusSuccess, null);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getSsid(
+ any(byte[].class), any(ISupplicantP2pIface.getSsidCallback.class));
+
+ assertNull(mDut.getSsid(mPeerMacAddress));
+ }
+
+ /**
+ * Verify that getSsid returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testGetSsid_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] address, ISupplicantP2pIface.getSsidCallback cb) {
+ cb.onValues(mStatusFailure, null);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).getSsid(
+ any(byte[].class), any(ISupplicantP2pIface.getSsidCallback.class));
+
+ assertNull(mDut.getSsid(mPeerMacAddress));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that getSsid disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testGetSsid_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doThrow(mRemoteException)
+ .when(mISupplicantP2pIfaceMock).getSsid(
+ any(byte[].class), any(ISupplicantP2pIface.getSsidCallback.class));
+
+ assertNull(mDut.getSsid(mPeerMacAddress));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for reinvoke()
+ */
+ @Test
+ public void testReinvoke_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.reinvoke(anyInt(), eq(mPeerMacAddressBytes)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.reinvoke(0, mPeerMacAddress));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.reinvoke(0, mPeerMacAddress));
+ }
+
+ /**
+ * Reinvoke with invalid arguments.
+ */
+ @Test
+ public void testReinvoke_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.reinvoke(anyInt(), any(byte[].class)))
+ .thenReturn(mStatusSuccess);
+
+ for (String address : mInvalidMacAddresses) {
+ assertFalse(mDut.reinvoke(0, address));
+ }
+ }
+
+ /**
+ * Verify that reinvoke returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testReinvoke_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.reinvoke(anyInt(), any(byte[].class)))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.reinvoke(0, mPeerMacAddress));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that reinvoke disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testReinvoke_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.reinvoke(anyInt(), any(byte[].class)))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.reinvoke(0, mPeerMacAddress));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for groupAdd()
+ */
+ @Test
+ public void testGroupAdd_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.addGroup(eq(true), eq(3)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.groupAdd(3, true));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.groupAdd(3, true));
+ }
+
+ /**
+ * Verify that groupAdd returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testGroupAdd_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.addGroup(anyBoolean(), anyInt()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.groupAdd(0, true));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that groupAdd disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testGroupAdd_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.addGroup(anyBoolean(), anyInt()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.groupAdd(0, true));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for groupRemove()
+ */
+ @Test
+ public void testGroupRemove_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.removeGroup(eq(mIfaceName)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.groupRemove(mIfaceName));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.groupRemove(mIfaceName));
+ }
+
+ /**
+ * Verify that groupRemove returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testGroupRemove_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.removeGroup(anyString()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.groupRemove(mIfaceName));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that groupRemove disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testGroupRemove_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.removeGroup(anyString()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.groupRemove(mIfaceName));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for getGroupCapability()
+ */
+ @Test
+ public void testGetGroupCapability_success() throws Exception {
+ final int caps = 123;
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] address, ISupplicantP2pIface.getGroupCapabilityCallback cb) {
+ cb.onValues(mStatusSuccess, caps);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock)
+ .getGroupCapability(
+ eq(mPeerMacAddressBytes),
+ any(ISupplicantP2pIface.getGroupCapabilityCallback.class));
+
+ // Default value when service is not initialized.
+ assertEquals(-1, mDut.getGroupCapability(mPeerMacAddress));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertEquals(caps, mDut.getGroupCapability(mPeerMacAddress));
+ }
+
+ /**
+ * GetGroupCapability with invalid arguments.
+ */
+ @Test
+ public void testGetGroupCapability_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] address, ISupplicantP2pIface.getGroupCapabilityCallback cb) {
+ cb.onValues(mStatusSuccess, 0);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock)
+ .getGroupCapability(
+ eq(mPeerMacAddressBytes),
+ any(ISupplicantP2pIface.getGroupCapabilityCallback.class));
+
+ for (String address : mInvalidMacAddresses) {
+ assertEquals(-1, mDut.getGroupCapability(address));
+ }
+ }
+
+ /**
+ * Verify that getGroupCapability returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testGetGroupCapability_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] address, ISupplicantP2pIface.getGroupCapabilityCallback cb) {
+ cb.onValues(mStatusFailure, 0);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock)
+ .getGroupCapability(
+ eq(mPeerMacAddressBytes),
+ any(ISupplicantP2pIface.getGroupCapabilityCallback.class));
+
+ assertEquals(-1, mDut.getGroupCapability(mPeerMacAddress));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that getGroupCapability disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testGetGroupCapability_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doThrow(mRemoteException)
+ .when(mISupplicantP2pIfaceMock)
+ .getGroupCapability(
+ eq(mPeerMacAddressBytes),
+ any(ISupplicantP2pIface.getGroupCapabilityCallback.class));
+ assertEquals(-1, mDut.getGroupCapability(mPeerMacAddress));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for configureExtListen()
+ */
+ @Test
+ public void testConfigureExtListen_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.configureExtListen(eq(123), eq(456)))
+ .thenReturn(mStatusSuccess);
+ when(mISupplicantP2pIfaceMock.configureExtListen(eq(0), eq(0)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.configureExtListen(true, 123, 456));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.configureExtListen(true, 123, 456));
+ // Turning listening off should reset intervals to 0s.
+ assertTrue(mDut.configureExtListen(false, 999, 999));
+ // Disable listening.
+ assertTrue(mDut.configureExtListen(false, -1, -1));
+ }
+
+ /**
+ * Test configureExtListen with invalid parameters.
+ */
+ @Test
+ public void testConfigureExtListen_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.configureExtListen(anyInt(), anyInt()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.configureExtListen(true, -1, 1));
+ assertFalse(mDut.configureExtListen(true, 1, -1));
+ }
+
+ /**
+ * Verify that configureExtListen returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testConfigureExtListen_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.configureExtListen(anyInt(), anyInt()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.configureExtListen(true, 1, 1));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that configureExtListen disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testConfigureExtListen_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.configureExtListen(anyInt(), anyInt()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.configureExtListen(true, 1, 1));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for setListenChannel()
+ */
+ @Test
+ public void testSetListenChannel_success() throws Exception {
+ int lc = 4;
+ int oc = 163;
+ ISupplicantP2pIface.FreqRange range1 = new ISupplicantP2pIface.FreqRange();
+ range1.min = 1000;
+ range1.max = 5810;
+ ISupplicantP2pIface.FreqRange range2 = new ISupplicantP2pIface.FreqRange();
+ range2.min = 5820;
+ range2.max = 6000;
+ ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>();
+ ranges.add(range1);
+ ranges.add(range2);
+
+ when(mISupplicantP2pIfaceMock.setListenChannel(eq(lc), anyInt()))
+ .thenReturn(mStatusSuccess);
+ when(mISupplicantP2pIfaceMock.setDisallowedFrequencies(eq(ranges)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.setListenChannel(lc, oc));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.setListenChannel(lc, oc));
+ }
+
+ /**
+ * Sunny day scenario for setListenChannel()
+ */
+ @Test
+ public void testSetListenChannel_successResetDisallowedFreq() throws Exception {
+ int lc = 2;
+ int oc = 0;
+ ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>();
+
+ when(mISupplicantP2pIfaceMock.setListenChannel(eq(lc), anyInt()))
+ .thenReturn(mStatusSuccess);
+ when(mISupplicantP2pIfaceMock.setDisallowedFrequencies(eq(ranges)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.setListenChannel(lc, oc));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.setListenChannel(lc, oc));
+ }
+
+ /**
+ * Test setListenChannel with invalid parameters.
+ */
+ @Test
+ public void testSetListenChannel_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setListenChannel(anyInt(), anyInt()))
+ .thenReturn(mStatusSuccess);
+ when(mISupplicantP2pIfaceMock.setDisallowedFrequencies(any(ArrayList.class)))
+ .thenReturn(mStatusSuccess);
+ assertFalse(mDut.setListenChannel(-1, 1));
+ assertFalse(mDut.setListenChannel(1, -1));
+ }
+
+ /**
+ * Verify that setListenChannel returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testSetListenChannel_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setListenChannel(anyInt(), anyInt()))
+ .thenReturn(mStatusFailure);
+ when(mISupplicantP2pIfaceMock.setDisallowedFrequencies(any(ArrayList.class)))
+ .thenReturn(mStatusSuccess);
+ assertFalse(mDut.setListenChannel(1, 1));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that setListenChannel disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testSetListenChannel_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setListenChannel(anyInt(), anyInt()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.setListenChannel(1, 1));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for serviceAdd()
+ */
+ @Test
+ public void testServiceAdd_success() throws Exception {
+ WifiP2pServiceInfo info = createDummyP2pServiceInfo(
+ mValidUpnpService, mValidBonjourService);
+ final HashSet<String> services = new HashSet<String>();
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int version, String name) {
+ services.add("upnp");
+ assertEquals(mValidUpnpServiceVersion, version);
+ assertEquals(mValidUpnpServiceName, name);
+ return mStatusSuccess;
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).addUpnpService(anyInt(), anyString());
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(ArrayList<Byte> request, ArrayList<Byte> response) {
+ services.add("bonjour");
+ assertEquals(mValidBonjourServiceRequest, request);
+ assertEquals(mValidBonjourServiceResponse, response);
+ return mStatusSuccess;
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).addBonjourService(
+ any(ArrayList.class), any(ArrayList.class));
+
+ // Default value when service is not initialized.
+ assertFalse(mDut.serviceAdd(info));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.serviceAdd(info));
+ // Confirm that both services have been added.
+ assertTrue(services.contains("upnp"));
+ assertTrue(services.contains("bonjour"));
+
+ // Empty services should cause no trouble.
+ assertTrue(mDut.serviceAdd(createDummyP2pServiceInfo()));
+ }
+
+ /**
+ * Test serviceAdd with invalid parameters.
+ */
+ @Test
+ public void testServiceAdd_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ when(mISupplicantP2pIfaceMock.addUpnpService(anyInt(), anyString()))
+ .thenReturn(mStatusSuccess);
+ when(mISupplicantP2pIfaceMock.addBonjourService(
+ any(ArrayList.class), any(ArrayList.class)))
+ .thenReturn(mStatusSuccess);
+
+ assertFalse(mDut.serviceAdd(null));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mInvalidService1)));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mInvalidService2)));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mInvalidUpnpService1)));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mInvalidUpnpService2)));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mInvalidUpnpService3)));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mInvalidBonjourService1)));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mInvalidBonjourService2)));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mInvalidBonjourService3)));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mInvalidBonjourService4)));
+ }
+
+ /**
+ * Verify that serviceAdd returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testServiceAdd_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ when(mISupplicantP2pIfaceMock.addUpnpService(anyInt(), anyString()))
+ .thenReturn(mStatusFailure);
+ when(mISupplicantP2pIfaceMock.addBonjourService(
+ any(ArrayList.class), any(ArrayList.class)))
+ .thenReturn(mStatusFailure);
+
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mValidUpnpService)));
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mValidBonjourService)));
+
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that serviceAdd disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testServiceAdd_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ when(mISupplicantP2pIfaceMock.addUpnpService(anyInt(), anyString()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mValidUpnpService)));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.addBonjourService(
+ any(ArrayList.class), any(ArrayList.class)))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.serviceAdd(createDummyP2pServiceInfo(mValidBonjourService)));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for serviceRemove()
+ */
+ @Test
+ public void testServiceRemove_success() throws Exception {
+ WifiP2pServiceInfo info = createDummyP2pServiceInfo(
+ mValidUpnpService, mValidBonjourService);
+ final HashSet<String> services = new HashSet<String>();
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(int version, String name) {
+ services.add("upnp");
+ assertEquals(mValidUpnpServiceVersion, version);
+ assertEquals(mValidUpnpServiceName, name);
+ return mStatusSuccess;
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).removeUpnpService(anyInt(), anyString());
+
+ doAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(ArrayList<Byte> request) {
+ services.add("bonjour");
+ assertEquals(mValidBonjourServiceRequest, request);
+ return mStatusSuccess;
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).removeBonjourService(any(ArrayList.class));
+
+ // Default value when service is not initialized.
+ assertFalse(mDut.serviceRemove(info));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.serviceRemove(info));
+ // Confirm that both services have been removed.
+ assertTrue(services.contains("upnp"));
+ assertTrue(services.contains("bonjour"));
+
+ // Empty services should cause no trouble.
+ assertTrue(mDut.serviceRemove(createDummyP2pServiceInfo()));
+ }
+
+ /**
+ * Test serviceRemove with invalid parameters.
+ */
+ @Test
+ public void testServiceRemove_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ when(mISupplicantP2pIfaceMock.removeUpnpService(anyInt(), anyString()))
+ .thenReturn(mStatusSuccess);
+ when(mISupplicantP2pIfaceMock.removeBonjourService(any(ArrayList.class)))
+ .thenReturn(mStatusSuccess);
+
+ assertFalse(mDut.serviceRemove(null));
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mInvalidService1)));
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mInvalidService2)));
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mInvalidUpnpService1)));
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mInvalidUpnpService2)));
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mInvalidUpnpService3)));
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mInvalidBonjourService1)));
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mInvalidBonjourService2)));
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mInvalidBonjourService3)));
+ // Response parameter is ignored by serviceRemove call, hence the following would pass.
+ // The production code would need to parse otherwise redundant parameter to fail on this
+ // one.
+ //
+ // assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mInvalidBonjourService4)));
+ }
+
+ /**
+ * Verify that serviceRemove returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testServiceRemove_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ when(mISupplicantP2pIfaceMock.removeUpnpService(anyInt(), anyString()))
+ .thenReturn(mStatusFailure);
+ when(mISupplicantP2pIfaceMock.removeBonjourService(any(ArrayList.class)))
+ .thenReturn(mStatusFailure);
+
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mValidUpnpService)));
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mValidBonjourService)));
+
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that serviceRemove disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testServiceRemove_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ when(mISupplicantP2pIfaceMock.removeUpnpService(anyInt(), anyString()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mValidUpnpService)));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.removeBonjourService(any(ArrayList.class)))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.serviceRemove(createDummyP2pServiceInfo(mValidBonjourService)));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for requestServiceDiscovery()
+ */
+ @Test
+ public void testRequestServiceDiscovery_success() throws Exception {
+ final int caps = 123;
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] address, ArrayList<Byte> query,
+ ISupplicantP2pIface.requestServiceDiscoveryCallback cb) {
+ cb.onValues(mStatusSuccess, 1234);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock)
+ .requestServiceDiscovery(
+ eq(mPeerMacAddressBytes),
+ eq(mValidBonjourServiceRequest),
+ any(ISupplicantP2pIface.requestServiceDiscoveryCallback.class));
+
+ // Default value when service is not initialized.
+ assertNull(mDut.requestServiceDiscovery(mPeerMacAddress, mValidServiceRequestString));
+
+ executeAndValidateInitializationSequence(false, false, false);
+ assertEquals("1234", mDut.requestServiceDiscovery(
+ mPeerMacAddress, mValidServiceRequestString));
+ }
+
+ /**
+ * RequestServiceDiscovery with invalid arguments.
+ */
+ @Test
+ public void testRequestServiceDiscovery_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(byte[] address, ArrayList<Byte> query,
+ ISupplicantP2pIface.requestServiceDiscoveryCallback cb) {
+ cb.onValues(mStatusSuccess, 0);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock)
+ .requestServiceDiscovery(
+ any(byte[].class), any(ArrayList.class),
+ any(ISupplicantP2pIface.requestServiceDiscoveryCallback.class));
+
+ for (String address : mInvalidMacAddresses) {
+ assertNull(mDut.requestServiceDiscovery(
+ address, mValidServiceRequestString));
+ }
+ assertNull(mDut.requestServiceDiscovery(mPeerMacAddress, null));
+ assertNull(mDut.requestServiceDiscovery(mPeerMacAddress, mInvalidServiceRequestString));
+ }
+
+ /**
+ * Verify that requestServiceDiscovery returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testRequestServiceDiscovery_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(
+ byte[] address, ArrayList<Byte> query,
+ ISupplicantP2pIface.requestServiceDiscoveryCallback cb) {
+ cb.onValues(mStatusFailure, 0);
+ }
+ })
+ .when(mISupplicantP2pIfaceMock)
+ .requestServiceDiscovery(
+ any(byte[].class), any(ArrayList.class),
+ any(ISupplicantP2pIface.requestServiceDiscoveryCallback.class));
+
+ assertNull(mDut.requestServiceDiscovery(mPeerMacAddress, mValidServiceRequestString));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that requestServiceDiscovery disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testRequestServiceDiscovery_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doThrow(mRemoteException)
+ .when(mISupplicantP2pIfaceMock)
+ .requestServiceDiscovery(
+ any(byte[].class), any(ArrayList.class),
+ any(ISupplicantP2pIface.requestServiceDiscoveryCallback.class));
+ assertNull(mDut.requestServiceDiscovery(mPeerMacAddress, mValidServiceRequestString));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+ // Test constant used in cancelServiceDiscovery tests
+ static final String SERVICE_IDENTIFIER_STR = "521918410304";
+ static final long SERVICE_IDENTIFIER_LONG = 521918410304L;
+
+ /**
+ * Sunny day scenario for cancelServiceDiscovery()
+ */
+ @Test
+ public void testCancelServiceDiscovery_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.cancelServiceDiscovery(SERVICE_IDENTIFIER_LONG))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.cancelServiceDiscovery(SERVICE_IDENTIFIER_STR));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.cancelServiceDiscovery(SERVICE_IDENTIFIER_STR));
+ }
+
+ /**
+ * Test cancelServiceDiscovery with invalid parameters.
+ */
+ @Test
+ public void testCancelServiceDiscovery_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.cancelServiceDiscovery(anyLong()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.cancelServiceDiscovery(null));
+ assertFalse(mDut.cancelServiceDiscovery("not a number"));
+ }
+
+ /**
+ * Verify that cancelServiceDiscovery returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testCancelServiceDiscovery_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.cancelServiceDiscovery(anyLong()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.cancelServiceDiscovery(SERVICE_IDENTIFIER_STR));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that cancelServiceDiscovery disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testCancelServiceDiscovery_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.cancelServiceDiscovery(anyLong()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.cancelServiceDiscovery(SERVICE_IDENTIFIER_STR));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for setMiracastMode()
+ */
+ @Test
+ public void testSetMiracastMode_success() throws Exception {
+ HashSet<Byte> modes = new HashSet<Byte>();
+
+ when(mISupplicantP2pIfaceMock.setMiracastMode(anyByte()))
+ .thenAnswer(new AnswerWithArguments() {
+ public SupplicantStatus answer(byte mode) {
+ modes.add(mode);
+ return mStatusSuccess;
+ }
+ });
+ // Default value when service is not initialized.
+ assertFalse(mDut.setMiracastMode(WifiP2pManager.MIRACAST_SOURCE));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.setMiracastMode(WifiP2pManager.MIRACAST_SOURCE));
+ assertTrue(modes.contains(ISupplicantP2pIface.MiracastMode.SOURCE));
+
+ assertTrue(mDut.setMiracastMode(WifiP2pManager.MIRACAST_SINK));
+ assertTrue(modes.contains(ISupplicantP2pIface.MiracastMode.SINK));
+
+ // Any invalid number yields disabled miracast mode.
+ assertTrue(mDut.setMiracastMode(-1));
+ assertTrue(modes.contains(ISupplicantP2pIface.MiracastMode.DISABLED));
+ }
+
+ /**
+ * Verify that setMiracastMode returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testSetMiracastMode_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setMiracastMode(anyByte()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.setMiracastMode(WifiP2pManager.MIRACAST_SOURCE));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that setMiracastMode disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testSetMiracastMode_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setMiracastMode(anyByte()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.setMiracastMode(WifiP2pManager.MIRACAST_SOURCE));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for startWpsPbc()
+ */
+ @Test
+ public void testStartWpsPbc_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.startWpsPbc(eq(mIfaceName), eq(mPeerMacAddressBytes)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.startWpsPbc(mIfaceName, mPeerMacAddress));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.startWpsPbc(mIfaceName, mPeerMacAddress));
+ }
+
+ /**
+ * StartWpsPbc with invalid arguments.
+ */
+ @Test
+ public void testStartWpsPbc_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.startWpsPbc(anyString(), any(byte[].class)))
+ .thenReturn(mStatusSuccess);
+
+ for (String address : mInvalidMacAddresses) {
+ assertFalse(mDut.startWpsPbc(mIfaceName, address));
+ }
+
+ assertFalse(mDut.startWpsPbc(null, mPeerMacAddress));
+ }
+
+ /**
+ * Verify that startWpsPbc returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testStartWpsPbc_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.startWpsPbc(anyString(), any(byte[].class)))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.startWpsPbc(mIfaceName, mPeerMacAddress));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that startWpsPbc disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testStartWpsPbc_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.startWpsPbc(anyString(), any(byte[].class)))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.startWpsPbc(mIfaceName, mPeerMacAddress));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for startWpsPinKeypad()
+ */
+ @Test
+ public void testStartWpsPinKeypad_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.startWpsPinKeypad(eq(mIfaceName), eq("1234")))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.startWpsPinKeypad(mIfaceName, "1234"));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.startWpsPinKeypad(mIfaceName, "1234"));
+ }
+
+ /**
+ * StartWpsPinKeypad with invalid arguments.
+ */
+ @Test
+ public void testStartWpsPinKeypad_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.startWpsPinKeypad(anyString(), anyString()))
+ .thenReturn(mStatusSuccess);
+
+ assertFalse(mDut.startWpsPinKeypad(null, "1234"));
+ assertFalse(mDut.startWpsPinKeypad(mIfaceName, null));
+ // StartWpsPinPinKeypad does not validate, that PIN indeed holds an integer encoded in a
+ // string. This code would be redundant, as HAL requires string to be passed.
+ }
+
+ /**
+ * Verify that startWpsPinKeypad returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testStartWpsPinKeypad_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.startWpsPinKeypad(anyString(), anyString()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.startWpsPinKeypad(mIfaceName, "1234"));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that startWpsPinKeypad disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testStartWpsPinKeypad_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.startWpsPinKeypad(anyString(), anyString()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.startWpsPinKeypad(mIfaceName, "1234"));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for startWpsPinDisplay()
+ */
+ @Test
+ public void testStartWpsPinDisplay_success() throws Exception {
+ doAnswer(new AnswerWithArguments() {
+ public void answer(String ifName, byte[] bssid,
+ ISupplicantP2pIface.startWpsPinDisplayCallback cb) {
+ cb.onValues(mStatusSuccess, "1234");
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).startWpsPinDisplay(
+ eq(mIfaceName), eq(mPeerMacAddressBytes),
+ any(ISupplicantP2pIface.startWpsPinDisplayCallback.class));
+
+ // Default value when service is not initialized.
+ assertNull(mDut.startWpsPinDisplay(mIfaceName, mPeerMacAddress));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertEquals("1234", mDut.startWpsPinDisplay(mIfaceName, mPeerMacAddress));
+ }
+
+ /**
+ * StartWpsPinDisplay with invalid arguments.
+ */
+ @Test
+ public void testStartWpsPinDisplay_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(String ifName, byte[] bssid,
+ ISupplicantP2pIface.startWpsPinDisplayCallback cb) {
+ cb.onValues(mStatusSuccess, "1234");
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).startWpsPinDisplay(
+ anyString(), any(byte[].class),
+ any(ISupplicantP2pIface.startWpsPinDisplayCallback.class));
+
+ for (String address : mInvalidMacAddresses) {
+ assertNull(mDut.startWpsPinDisplay(mIfaceName, address));
+ }
+
+ assertNull(mDut.startWpsPinDisplay(null, mPeerMacAddress));
+ }
+
+ /**
+ * Verify that startWpsPinDisplay returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testStartWpsPinDisplay_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(String ifName, byte[] bssid,
+ ISupplicantP2pIface.startWpsPinDisplayCallback cb) {
+ cb.onValues(mStatusFailure, "1234");
+ }
+ })
+ .when(mISupplicantP2pIfaceMock).startWpsPinDisplay(
+ anyString(), any(byte[].class),
+ any(ISupplicantP2pIface.startWpsPinDisplayCallback.class));
+
+ assertNull(mDut.startWpsPinDisplay(mIfaceName, mPeerMacAddress));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that startWpsPinDisplay disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testStartWpsPinDisplay_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ doThrow(mRemoteException)
+ .when(mISupplicantP2pIfaceMock).startWpsPinDisplay(
+ anyString(), any(byte[].class),
+ any(ISupplicantP2pIface.startWpsPinDisplayCallback.class));
+ assertNull(mDut.startWpsPinDisplay(mIfaceName, mPeerMacAddress));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for cancelWps()
+ */
+ @Test
+ public void testCancelWps_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.cancelWps(eq(mIfaceName)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.cancelWps(mIfaceName));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.cancelWps(mIfaceName));
+ }
+
+ /**
+ * CancelWps with invalid arguments.
+ */
+ @Test
+ public void testCancelWps_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.cancelWps(anyString()))
+ .thenReturn(mStatusSuccess);
+
+ assertFalse(mDut.cancelWps(null));
+ }
+
+ /**
+ * Verify that cancelWps returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testCancelWps_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.cancelWps(anyString()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.cancelWps(mIfaceName));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that cancelWps disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testCancelWps_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.cancelWps(anyString()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.cancelWps(mIfaceName));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for enableWfd()
+ */
+ @Test
+ public void testEnableWfd_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.enableWfd(eq(true)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.enableWfd(true));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.enableWfd(true));
+ }
+
+ /**
+ * Verify that enableWfd returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testEnableWfd_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.enableWfd(anyBoolean()))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.enableWfd(true));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that enableWfd disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testEnableWfd_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.enableWfd(anyBoolean()))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.enableWfd(false));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+
+ /**
+ * Sunny day scenario for setWfdDeviceInfo()
+ */
+ @Test
+ public void testSetWfdDeviceInfo_success() throws Exception {
+ when(mISupplicantP2pIfaceMock.setWfdDeviceInfo(eq(mValidServiceRequestBytes)))
+ .thenReturn(mStatusSuccess);
+ // Default value when service is not initialized.
+ assertFalse(mDut.setWfdDeviceInfo(mValidServiceRequestString));
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.setWfdDeviceInfo(mValidServiceRequestString));
+ }
+
+ /**
+ * SetWfdDeviceInfo with invalid arguments.
+ */
+ @Test
+ public void testSetWfdDeviceInfo_invalidArguments() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setWfdDeviceInfo(any(byte[].class)))
+ .thenReturn(mStatusSuccess);
+
+ assertFalse(mDut.setWfdDeviceInfo(null));
+ assertFalse(mDut.setWfdDeviceInfo(mInvalidServiceRequestString));
+ }
+
+ /**
+ * Verify that setWfdDeviceInfo returns false, if status is not SUCCESS.
+ */
+ @Test
+ public void testSetWfdDeviceInfo_failure() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setWfdDeviceInfo(any(byte[].class)))
+ .thenReturn(mStatusFailure);
+ assertFalse(mDut.setWfdDeviceInfo(mValidServiceRequestString));
+ // Check that service is still alive.
+ assertTrue(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify that setWfdDeviceInfo disconnects and returns false, if HAL throws exception.
+ */
+ @Test
+ public void testSetWfdDeviceInfo_exception() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+ when(mISupplicantP2pIfaceMock.setWfdDeviceInfo(any(byte[].class)))
+ .thenThrow(mRemoteException);
+ assertFalse(mDut.setWfdDeviceInfo(mValidServiceRequestString));
+ // Check service is dead.
+ assertFalse(mDut.isInitializationComplete());
+ }
+
+ /**
+ * Verify the loading of group info.
+ * Specifically, all groups returned by listNetworks are added as a persistent group, so long as
+ * they are NOT current.
+ */
+ @Test
+ public void testLoadGroups() throws Exception {
+ executeAndValidateInitializationSequence(false, false, false);
+
+ // Class to hold the P2p group info returned from the HIDL interface.
+ class P2pGroupInfo {
+ public String ssid;
+ public byte[] bssid;
+ public boolean isGo;
+ public boolean isCurrent;
+ P2pGroupInfo(String ssid, byte[] bssid, boolean isGo, boolean isCurrent) {
+ this.ssid = ssid;
+ this.bssid = bssid;
+ this.isGo = isGo;
+ this.isCurrent = isCurrent;
+ }
+ }
+
+ Map<Integer, P2pGroupInfo> groups = new HashMap<>();
+ groups.put(0, new P2pGroupInfo(
+ "test_34",
+ NativeUtil.macAddressToByteArray("56:34:ab:12:12:34"),
+ false, false));
+ groups.put(1, new P2pGroupInfo(
+ "test_1234",
+ NativeUtil.macAddressToByteArray("16:ed:ab:12:45:34"),
+ true, false));
+ groups.put(2, new P2pGroupInfo(
+ "test_4545",
+ NativeUtil.macAddressToByteArray("32:89:23:56:45:34"),
+ true, false));
+ groups.put(3, new P2pGroupInfo(
+ "iShouldntBeHere",
+ NativeUtil.macAddressToByteArray("aa:bb:cc:56:45:34"),
+ true, true));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pIface.listNetworksCallback cb) {
+ cb.onValues(mStatusSuccess, new ArrayList<Integer>(groups.keySet()));
+ }
+ }).when(mISupplicantP2pIfaceMock)
+ .listNetworks(any(ISupplicantP2pIface.listNetworksCallback.class));
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantP2pIface.getNetworkCallback cb) {
+ try {
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pNetwork.getSsidCallback cb) {
+ cb.onValues(mStatusSuccess,
+ NativeUtil.stringToByteArrayList(groups.get(networkId).ssid));
+ return;
+ }
+ }).when(mISupplicantP2pNetworkMock)
+ .getSsid(any(ISupplicantP2pNetwork.getSsidCallback.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pNetwork.getBssidCallback cb) {
+ cb.onValues(mStatusSuccess, groups.get(networkId).bssid);
+ return;
+ }
+ }).when(mISupplicantP2pNetworkMock)
+ .getBssid(any(ISupplicantP2pNetwork.getBssidCallback.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pNetwork.isCurrentCallback cb) {
+ cb.onValues(mStatusSuccess, groups.get(networkId).isCurrent);
+ return;
+ }
+ }).when(mISupplicantP2pNetworkMock)
+ .isCurrent(any(ISupplicantP2pNetwork.isCurrentCallback.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pNetwork.isGoCallback cb) {
+ cb.onValues(mStatusSuccess, groups.get(networkId).isGo);
+ return;
+ }
+ }).when(mISupplicantP2pNetworkMock)
+ .isGo(any(ISupplicantP2pNetwork.isGoCallback.class));
+ } catch (RemoteException e) {
+ }
+ cb.onValues(mStatusSuccess, mISupplicantP2pNetworkMock);
+ return;
+ }
+ }).when(mISupplicantP2pIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantP2pIface.getNetworkCallback.class));
+
+ WifiP2pGroupList p2pGroups = new WifiP2pGroupList();
+ assertTrue(mDut.loadGroups(p2pGroups));
+
+ assertEquals(3, p2pGroups.getGroupList().size());
+ for (WifiP2pGroup group : p2pGroups.getGroupList()) {
+ int networkId = group.getNetworkId();
+ assertEquals(groups.get(networkId).ssid, group.getNetworkName());
+ assertEquals(
+ NativeUtil.macAddressFromByteArray(groups.get(networkId).bssid),
+ group.getOwner().deviceAddress);
+ assertEquals(groups.get(networkId).isGo, group.isGroupOwner());
+ }
+ }
+
+ /**
+ * Sunny day scenario for setClientList()
+ */
+ @Test
+ public void testSetClientList() throws Exception {
+ int testNetworkId = 5;
+ final String client1 = mGroupOwnerMacAddress;
+ final String client2 = mPeerMacAddress;
+
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantP2pIface.getNetworkCallback cb) {
+ if (networkId == testNetworkId) {
+ cb.onValues(mStatusSuccess, mISupplicantP2pNetworkMock);
+ } else {
+ cb.onValues(mStatusFailure, null);
+ }
+ return;
+ }
+ }).when(mISupplicantP2pIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantP2pIface.getNetworkCallback.class));
+ when(mISupplicantP2pNetworkMock.setClientList(any(ArrayList.class)))
+ .thenReturn(mStatusSuccess);
+
+ String clientList = client1 + " " + client2;
+ assertTrue(mDut.setClientList(testNetworkId, clientList));
+ verify(mISupplicantP2pIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantP2pIface.getNetworkCallback.class));
+ ArgumentCaptor<ArrayList> capturedClients = ArgumentCaptor.forClass(ArrayList.class);
+ verify(mISupplicantP2pNetworkMock).setClientList(capturedClients.capture());
+
+ // Convert these to long to help with comparisons.
+ ArrayList<byte[]> clients = capturedClients.getValue();
+ ArrayList<Long> expectedClients = new ArrayList<Long>() {{
+ add(NativeUtil.macAddressToLong(mGroupOwnerMacAddressBytes));
+ add(NativeUtil.macAddressToLong(mPeerMacAddressBytes));
+ }};
+ ArrayList<Long> receivedClients = new ArrayList<Long>();
+ for (byte[] client : clients) {
+ receivedClients.add(NativeUtil.macAddressToLong(client));
+ }
+ assertEquals(expectedClients, receivedClients);
+ }
+
+ /**
+ * Failure scenario for setClientList() when getNetwork returns null.
+ */
+ @Test
+ public void testSetClientListFailureDueToGetNetwork() throws Exception {
+ int testNetworkId = 5;
+ final String client1 = mGroupOwnerMacAddress;
+ final String client2 = mPeerMacAddress;
+
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantP2pIface.getNetworkCallback cb) {
+ cb.onValues(mStatusFailure, null);
+ return;
+ }
+ }).when(mISupplicantP2pIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantP2pIface.getNetworkCallback.class));
+ when(mISupplicantP2pNetworkMock.setClientList(any(ArrayList.class)))
+ .thenReturn(mStatusSuccess);
+
+ String clientList = client1 + " " + client2;
+ assertFalse(mDut.setClientList(testNetworkId, clientList));
+ verify(mISupplicantP2pIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantP2pIface.getNetworkCallback.class));
+ verify(mISupplicantP2pNetworkMock, never()).setClientList(any(ArrayList.class));
+ }
+
+ /**
+ * Sunny day scenario for getClientList()
+ */
+ @Test
+ public void testGetClientList() throws Exception {
+ int testNetworkId = 5;
+ final String client1 = mGroupOwnerMacAddress;
+ final String client2 = mPeerMacAddress;
+
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantP2pIface.getNetworkCallback cb) {
+ if (networkId == testNetworkId) {
+ cb.onValues(mStatusSuccess, mISupplicantP2pNetworkMock);
+ } else {
+ cb.onValues(mStatusFailure, null);
+ }
+ return;
+ }
+ }).when(mISupplicantP2pIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantP2pIface.getNetworkCallback.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pNetwork.getClientListCallback cb) {
+ ArrayList<byte[]> clients = new ArrayList<byte[]>() {{
+ add(mGroupOwnerMacAddressBytes);
+ add(mPeerMacAddressBytes);
+ }};
+ cb.onValues(mStatusSuccess, clients);
+ return;
+ }
+ }).when(mISupplicantP2pNetworkMock)
+ .getClientList(any(ISupplicantP2pNetwork.getClientListCallback.class));
+
+ String clientList = client1 + " " + client2;
+ assertEquals(clientList, mDut.getClientList(testNetworkId));
+ verify(mISupplicantP2pIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantP2pIface.getNetworkCallback.class));
+ verify(mISupplicantP2pNetworkMock)
+ .getClientList(any(ISupplicantP2pNetwork.getClientListCallback.class));
+ }
+
+ /**
+ * Failure scenario for getClientList() when getNetwork returns null.
+ */
+ @Test
+ public void testGetClientListFailureDueToGetNetwork() throws Exception {
+ int testNetworkId = 5;
+ final String client1 = mGroupOwnerMacAddress;
+ final String client2 = mPeerMacAddress;
+
+ executeAndValidateInitializationSequence(false, false, false);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(final int networkId, ISupplicantP2pIface.getNetworkCallback cb) {
+ cb.onValues(mStatusFailure, null);
+ return;
+ }
+ }).when(mISupplicantP2pIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantP2pIface.getNetworkCallback.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicantP2pNetwork.getClientListCallback cb) {
+ ArrayList<byte[]> clients = new ArrayList<byte[]>() {{
+ add(mGroupOwnerMacAddressBytes);
+ add(mPeerMacAddressBytes);
+ }};
+ cb.onValues(mStatusSuccess, clients);
+ return;
+ }
+ }).when(mISupplicantP2pNetworkMock)
+ .getClientList(any(ISupplicantP2pNetwork.getClientListCallback.class));
+
+ assertEquals(null, mDut.getClientList(testNetworkId));
+ verify(mISupplicantP2pIfaceMock)
+ .getNetwork(anyInt(), any(ISupplicantP2pIface.getNetworkCallback.class));
+ verify(mISupplicantP2pNetworkMock, never())
+ .getClientList(any(ISupplicantP2pNetwork.getClientListCallback.class));
+ }
+
+ /**
+ * Sunny day scenario for saveConfig()
+ */
+ @Test
+ public void testSaveConfig() throws Exception {
+ when(mISupplicantP2pIfaceMock.saveConfig()).thenReturn(mStatusSuccess);
+
+ // Should fail before initialization.
+ assertFalse(mDut.saveConfig());
+ executeAndValidateInitializationSequence(false, false, false);
+ assertTrue(mDut.saveConfig());
+ verify(mISupplicantP2pIfaceMock).saveConfig();
+ }
+
+ /**
+ * Calls.initialize(), mocking various call back answers and verifying flow, asserting for the
+ * expected result. Verifies if ISupplicantP2pIface manager is initialized or reset.
+ * Each of the arguments will cause a different failure mode when set true.
+ */
+ private void executeAndValidateInitializationSequence(boolean causeRemoteException,
+ boolean getZeroInterfaces, boolean getNullInterface) throws Exception {
+ boolean shouldSucceed = !causeRemoteException && !getZeroInterfaces && !getNullInterface;
+ // Setup callback mock answers
+ ArrayList<ISupplicant.IfaceInfo> interfaces;
+ if (getZeroInterfaces) {
+ interfaces = new ArrayList<ISupplicant.IfaceInfo>();
+ } else {
+ interfaces = mIfaceInfoList;
+ }
+
+ doAnswer(new AnswerWithArguments() {
+ public void answer(ISupplicant.listInterfacesCallback cb) throws RemoteException {
+ cb.onValues(mStatusSuccess, interfaces);
+ }
+ })
+ .when(mISupplicantMock).listInterfaces(any(ISupplicant.listInterfacesCallback.class));
+
+ if (causeRemoteException) {
+ doThrow(new RemoteException("Some error!!!"))
+ .when(mISupplicantMock).getInterface(any(ISupplicant.IfaceInfo.class),
+ any(ISupplicant.getInterfaceCallback.class));
+ } else {
+ doAnswer(new GetGetInterfaceAnswer(getNullInterface))
+ .when(mISupplicantMock).getInterface(any(ISupplicant.IfaceInfo.class),
+ any(ISupplicant.getInterfaceCallback.class));
+ }
+
+ mInOrder = inOrder(mServiceManagerMock, mISupplicantMock);
+ // Initialize SupplicantP2pIfaceHal, should call serviceManager.registerForNotifications
+ assertTrue(mDut.initialize());
+ // verify: service manager initialization sequence
+ mInOrder.verify(mServiceManagerMock).linkToDeath(any(IHwBinder.DeathRecipient.class),
+ anyLong());
+ mInOrder.verify(mServiceManagerMock).registerForNotifications(
+ eq(ISupplicant.kInterfaceName), eq(""), mServiceNotificationCaptor.capture());
+ // act: cause the onRegistration(...) callback to execute
+ mServiceNotificationCaptor.getValue().onRegistration(ISupplicant.kInterfaceName, "", true);
+
+ assertEquals(shouldSucceed, mDut.isInitializationComplete());
+ // verify: listInterfaces is called
+ mInOrder.verify(mISupplicantMock).listInterfaces(
+ any(ISupplicant.listInterfacesCallback.class));
+ if (!getZeroInterfaces) {
+ mInOrder.verify(mISupplicantMock)
+ .getInterface(any(ISupplicant.IfaceInfo.class),
+ any(ISupplicant.getInterfaceCallback.class));
+ }
+ }
+
+
+ private SupplicantStatus createSupplicantStatus(int code) {
+ SupplicantStatus status = new SupplicantStatus();
+ status.code = code;
+ return status;
+ }
+
+ /**
+ * Create an IfaceInfo with given type and name
+ */
+ private ISupplicant.IfaceInfo createIfaceInfo(int type, String name) {
+ ISupplicant.IfaceInfo info = new ISupplicant.IfaceInfo();
+ info.type = type;
+ info.name = name;
+ return info;
+ }
+
+ /**
+ * Create new dummy WifiP2pConfig instance.
+ */
+ private WifiP2pConfig createDummyP2pConfig(String peerAddress, int wpsProvMethod, String pin) {
+ WifiP2pConfig config = new WifiP2pConfig();
+ config.wps = new WpsInfo();
+ config.deviceAddress = peerAddress;
+
+ config.wps.setup = wpsProvMethod;
+ config.wps.pin = pin;
+
+ return config;
+ }
+
+ /**
+ * Create new dummy WifiP2pGroup instance.
+ */
+ private WifiP2pGroup createDummyP2pGroup() {
+ WifiP2pGroup group = new WifiP2pGroup();
+ group.setInterface(mIfaceName);
+
+ WifiP2pDevice owner = new WifiP2pDevice();
+ owner.deviceAddress = mGroupOwnerMacAddress;
+ group.setOwner(owner);
+
+ return group;
+ }
+
+ /**
+ * Create new dummy WifiP2pServiceInfo instance.
+ */
+ private WifiP2pServiceInfo createDummyP2pServiceInfo(String... services) {
+ class TestP2pServiceInfo extends WifiP2pServiceInfo {
+ TestP2pServiceInfo(String[] services) {
+ super(Arrays.asList(services));
+ }
+ }
+ return new TestP2pServiceInfo(services);
+ }
+
+ private class GetGetInterfaceAnswer extends AnswerWithArguments {
+ boolean mGetNullInterface;
+
+ GetGetInterfaceAnswer(boolean getNullInterface) {
+ mGetNullInterface = getNullInterface;
+ }
+
+ public void answer(ISupplicant.IfaceInfo iface, ISupplicant.getInterfaceCallback cb) {
+ if (mGetNullInterface) {
+ cb.onValues(mStatusSuccess, null);
+ } else {
+ cb.onValues(mStatusSuccess, mISupplicantIfaceMock);
+ }
+ }
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMonitorTest.java b/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMonitorTest.java
new file mode 100644
index 0000000..c2c3473
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pMonitorTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.p2p;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.WifiInjector;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.WifiP2pMonitor}.
+ */
+@SmallTest
+public class WifiP2pMonitorTest {
+ private static final String P2P_IFACE_NAME = "p2p0";
+ private static final String SECOND_P2P_IFACE_NAME = "p2p1";
+ private WifiP2pMonitor mWifiP2pMonitor;
+ private TestLooper mLooper;
+ private Handler mHandlerSpy;
+ private Handler mSecondHandlerSpy;
+
+ @Before
+ public void setUp() throws Exception {
+ mWifiP2pMonitor = new WifiP2pMonitor(mock(WifiInjector.class));
+ mLooper = new TestLooper();
+ mHandlerSpy = spy(new Handler(mLooper.getLooper()));
+ mSecondHandlerSpy = spy(new Handler(mLooper.getLooper()));
+ mWifiP2pMonitor.setMonitoring(P2P_IFACE_NAME, true);
+ }
+
+ /**
+ * Broadcast message test.
+ */
+ @Test
+ public void testBroadcastSupplicantDisconnectionEvent() {
+ mWifiP2pMonitor.registerHandler(
+ P2P_IFACE_NAME, WifiP2pMonitor.SUP_DISCONNECTION_EVENT, mHandlerSpy);
+ mWifiP2pMonitor.broadcastSupplicantDisconnectionEvent(P2P_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiP2pMonitor.SUP_DISCONNECTION_EVENT, messageCaptor.getValue().what);
+ }
+ /**
+ * Broadcast message to two handlers test.
+ */
+ @Test
+ public void testBroadcastEventToTwoHandlers() {
+ mWifiP2pMonitor.registerHandler(
+ P2P_IFACE_NAME, WifiP2pMonitor.SUP_CONNECTION_EVENT, mHandlerSpy);
+ mWifiP2pMonitor.registerHandler(
+ P2P_IFACE_NAME, WifiP2pMonitor.SUP_CONNECTION_EVENT, mSecondHandlerSpy);
+ mWifiP2pMonitor.broadcastSupplicantConnectionEvent(P2P_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiP2pMonitor.SUP_CONNECTION_EVENT, messageCaptor.getValue().what);
+ verify(mSecondHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiP2pMonitor.SUP_CONNECTION_EVENT, messageCaptor.getValue().what);
+ }
+ /**
+ * Broadcast message when iface is null.
+ */
+ @Test
+ public void testBroadcastEventWhenIfaceIsNull() {
+ mWifiP2pMonitor.registerHandler(
+ P2P_IFACE_NAME, WifiP2pMonitor.SUP_DISCONNECTION_EVENT, mHandlerSpy);
+ mWifiP2pMonitor.broadcastSupplicantDisconnectionEvent(null);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiP2pMonitor.SUP_DISCONNECTION_EVENT, messageCaptor.getValue().what);
+ }
+ /**
+ * Broadcast message when iface handler is null.
+ */
+ @Test
+ public void testBroadcastEventWhenIfaceHandlerIsNull() {
+ mWifiP2pMonitor.registerHandler(
+ P2P_IFACE_NAME, WifiP2pMonitor.SUP_DISCONNECTION_EVENT, mHandlerSpy);
+ mWifiP2pMonitor.broadcastSupplicantDisconnectionEvent(SECOND_P2P_IFACE_NAME);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandlerSpy).handleMessage(messageCaptor.capture());
+ assertEquals(WifiP2pMonitor.SUP_DISCONNECTION_EVENT, messageCaptor.getValue().what);
+ }
+}
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 767cddf..60e5256 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 boolean isAllChannelsScanned(int band) {
@@ -98,7 +98,7 @@
protected Set<Integer> expectedBandScanFreqs(int band) {
ChannelCollection collection = mScanner.getChannelHelper().createChannelCollection();
collection.addBand(band);
- return collection.getSupplicantScanFreqs();
+ return collection.getScanFreqs();
}
protected Set<Integer> expectedBandAndChannelScanFreqs(int band, int... channels) {
@@ -107,7 +107,7 @@
for (int channel : channels) {
collection.addChannel(channel);
}
- return collection.getSupplicantScanFreqs();
+ return collection.getScanFreqs();
}
@Test
@@ -120,7 +120,7 @@
.build();
doSuccessfulSingleScanTest(settings, expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ),
- new HashSet<Integer>(),
+ new HashSet<String>(),
ScanResults.create(0, isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ),
2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450), false);
}
@@ -134,7 +134,7 @@
.build();
doSuccessfulSingleScanTest(settings, createFreqSet(5650),
- new HashSet<Integer>(),
+ new HashSet<String>(),
ScanResults.create(0, 5650, 5650, 5650, 5650, 5650, 5650, 5650, 5650), false);
}
@@ -150,7 +150,7 @@
.build();
doSuccessfulSingleScanTest(settings, expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ),
- new HashSet<Integer>(),
+ new HashSet<String>(),
ScanResults.create(0, isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ),
2400, 2450, 2450, 2400, 2450, 2450, 2400, 2450, 2450), true);
}
@@ -161,43 +161,48 @@
*/
@Test
public void singleScanSuccessWithHiddenNetworkIds() {
- int[] hiddenNetworkIds = {0, 5};
+ String[] hiddenNetworkSSIDs = {"test_ssid_1", "test_ssid_2"};
WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
.withBasePeriod(10000)
.withMaxApPerScan(10)
- .withHiddenNetworkIds(hiddenNetworkIds)
+ .withHiddenNetworkSSIDs(hiddenNetworkSSIDs)
.addBucketWithChannels(20000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, 5650)
.build();
- Set<Integer> hiddenNetworkIdSet = new HashSet<Integer>();
- for (int i = 0; i < hiddenNetworkIds.length; i++) {
- hiddenNetworkIdSet.add(hiddenNetworkIds[i]);
+ Set<String> hiddenNetworkSSIDSet = new HashSet<>();
+ for (int i = 0; i < hiddenNetworkSSIDs.length; i++) {
+ hiddenNetworkSSIDSet.add(hiddenNetworkSSIDs[i]);
}
doSuccessfulSingleScanTest(settings, createFreqSet(5650),
- hiddenNetworkIdSet,
+ hiddenNetworkSSIDSet,
ScanResults.create(0, 5650, 5650, 5650, 5650, 5650, 5650, 5650, 5650), false);
}
/**
* Tests whether the provided hidden networkId's in scan settings is truncated to max size
- * supported by wpa_supplicant when invoking native scan.
+ * supported by wificond when invoking native scan.
*/
@Test
public void singleScanSuccessWithTruncatedHiddenNetworkIds() {
- int[] hiddenNetworkIds = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
+ String[] hiddenNetworkSSIDs = {
+ "test_ssid_0", "test_ssid_1", "test_ssid_2", "test_ssid_3", "test_ssid_4",
+ "test_ssid_5", "test_ssid_6", "test_ssid_7", "test_ssid_8", "test_ssid_9",
+ "test_ssid_10", "test_ssid_11", "test_ssid_12", "test_ssid_13", "test_ssid_14",
+ "test_ssid_15", "test_ssid_16", "test_ssid_17", "test_ssid_18", "test_ssid_19"
+ };
WifiNative.ScanSettings settings = new NativeScanSettingsBuilder()
.withBasePeriod(10000)
.withMaxApPerScan(10)
- .withHiddenNetworkIds(hiddenNetworkIds)
+ .withHiddenNetworkSSIDs(hiddenNetworkSSIDs)
.addBucketWithChannels(20000, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, 5650)
.build();
- Set<Integer> hiddenNetworkIdSet = new HashSet<Integer>();
- for (int i = 0; i < SupplicantWifiScannerImpl.MAX_HIDDEN_NETWORK_IDS_PER_SCAN; i++) {
- hiddenNetworkIdSet.add(hiddenNetworkIds[i]);
+ Set<String> hiddenNetworkSSIDSet = new HashSet<>();
+ for (int i = 0; i < WificondScannerImpl.MAX_HIDDEN_NETWORK_IDS_PER_SCAN; i++) {
+ hiddenNetworkSSIDSet.add(hiddenNetworkSSIDs[i]);
}
doSuccessfulSingleScanTest(settings, createFreqSet(5650),
- hiddenNetworkIdSet,
+ hiddenNetworkSSIDSet,
ScanResults.create(0, 5650, 5650, 5650, 5650, 5650, 5650, 5650, 5650), false);
}
@@ -220,7 +225,7 @@
WifiNative.ScanEventHandler eventHandler2 = mock(WifiNative.ScanEventHandler.class);
// scan start succeeds
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
assertTrue(mScanner.startSingleScan(settings, eventHandler));
assertFalse("second scan while first scan running should fail immediately",
@@ -243,7 +248,7 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// scan fails
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(false);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(false);
// start scan
assertTrue(mScanner.startSingleScan(settings, eventHandler));
@@ -273,14 +278,14 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// scan succeeds
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// start scan
assertTrue(mScanner.startSingleScan(settings, eventHandler));
mLooper.dispatchAll();
// Fire timeout
- mAlarmManager.dispatch(SupplicantWifiScannerImpl.TIMEOUT_ALARM_TAG);
+ mAlarmManager.dispatch(WificondScannerImpl.TIMEOUT_ALARM_TAG);
mLooper.dispatchAll();
order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_FAILED);
@@ -289,7 +294,7 @@
}
/**
- * Test that a scan failure is reported if supplicant sends a scan failed event
+ * Test that a scan failure is reported if wificond sends a scan failed event
*/
@Test
public void singleScanFailOnFailedEvent() {
@@ -307,7 +312,7 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// scan succeeds
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// start scan
assertTrue(mScanner.startSingleScan(settings, eventHandler));
@@ -361,14 +366,14 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// scans succeed
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// start first scan
assertTrue(mScanner.startSingleScan(settings, eventHandler));
expectSuccessfulSingleScan(order, eventHandler,
expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ),
- new HashSet<Integer>(),
+ new HashSet<String>(),
ScanResults.create(0, isAllChannelsScanned(WifiScanner.WIFI_BAND_24_GHZ),
2400, 2450, 2450), false);
@@ -377,7 +382,7 @@
expectSuccessfulSingleScan(order, eventHandler,
expectedBandScanFreqs(WifiScanner.WIFI_BAND_BOTH_WITH_DFS),
- new HashSet<Integer>(),
+ new HashSet<String>(),
ScanResults.create(0, true,
5150, 5175), false);
@@ -385,7 +390,7 @@
}
/**
- * Validate that scan results that are returned from supplicant, which are timestamped prior to
+ * Validate that scan results that are returned from wificond, which are timestamped prior to
* the start of the scan, are ignored.
*/
@Test
@@ -399,7 +404,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,
@@ -434,7 +439,7 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// scan succeeds
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// start scan
assertTrue(mScanner.startSingleScan(settings, eventHandler));
@@ -479,19 +484,19 @@
}
protected void doSuccessfulSingleScanTest(WifiNative.ScanSettings settings,
- Set<Integer> expectedScan, Set<Integer> expectedHiddenNetIds, ScanResults results,
+ Set<Integer> expectedScan, Set<String> expectedHiddenNetSSIDs, ScanResults results,
boolean expectFullResults) {
WifiNative.ScanEventHandler eventHandler = mock(WifiNative.ScanEventHandler.class);
InOrder order = inOrder(eventHandler, mWifiNative);
// scan succeeds
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// start scan
assertTrue(mScanner.startSingleScan(settings, eventHandler));
- expectSuccessfulSingleScan(order, eventHandler, expectedScan, expectedHiddenNetIds,
+ expectSuccessfulSingleScan(order, eventHandler, expectedScan, expectedHiddenNetSSIDs,
results, expectFullResults);
verifyNoMoreInteractions(eventHandler);
@@ -499,8 +504,8 @@
protected void expectSuccessfulSingleScan(InOrder order,
WifiNative.ScanEventHandler eventHandler, Set<Integer> expectedScan,
- Set<Integer> expectedHiddenNetIds, ScanResults results, boolean expectFullResults) {
- order.verify(mWifiNative).scan(eq(expectedScan), eq(expectedHiddenNetIds));
+ Set<String> expectedHiddenNetSSIDs, ScanResults results, boolean expectFullResults) {
+ order.verify(mWifiNative).scan(eq(expectedScan), eq(expectedHiddenNetSSIDs));
when(mWifiNative.getScanResults()).thenReturn(results.getScanDetailArrayList());
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/HalWifiScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/HalWifiScannerTest.java
index d5ff877..e8138ca 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/HalWifiScannerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/HalWifiScannerTest.java
@@ -34,6 +34,7 @@
new int[]{2400, 2450},
new int[]{5150, 5175},
new int[]{5600, 5650});
- mScanner = new HalWifiScannerImpl(mContext, mWifiNative, mLooper.getLooper(), mClock);
+ mScanner = new HalWifiScannerImpl(mContext, mWifiNative, mWifiMonitor, mLooper.getLooper(),
+ mClock);
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java
index 3e482a9..de3f7ff 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/KnownBandsChannelHelperTest.java
@@ -285,7 +285,7 @@
assertThat(bucketSettings, channelsAre());
assertEquals(Collections.<Integer>emptySet(),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertTrue(mChannelCollection.isEmpty());
assertFalse(mChannelCollection.containsChannel(2400));
@@ -306,7 +306,7 @@
assertThat(bucketSettings, channelsAre());
assertEquals(Collections.<Integer>emptySet(),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertTrue(mChannelCollection.isEmpty());
assertFalse(mChannelCollection.containsChannel(2400));
@@ -326,7 +326,7 @@
assertThat(bucketSettings, bandIs(WifiScanner.WIFI_BAND_24_GHZ));
assertEquals(new HashSet<Integer>(Arrays.asList(2400, 2450)),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -346,7 +346,7 @@
assertThat(bucketSettings, channelsAre(2400));
assertEquals(new HashSet<Integer>(Arrays.asList(2400)),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -367,7 +367,7 @@
assertThat(bucketSettings, channelsAre(2400, 2450));
assertEquals(new HashSet<Integer>(Arrays.asList(2400, 2450)),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -388,7 +388,7 @@
assertThat(bucketSettings, bandIs(WifiScanner.WIFI_BAND_24_GHZ));
assertEquals(new HashSet<Integer>(Arrays.asList(2400, 2450)),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -409,7 +409,7 @@
assertThat(bucketSettings, channelsAre(2400, 2450, 5150));
assertEquals(new HashSet<Integer>(Arrays.asList(2400, 2450, 5150)),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -429,7 +429,7 @@
mChannelCollection.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
assertThat(bucketSettings, bandIs(WifiScanner.WIFI_BAND_BOTH_WITH_DFS));
- assertNull(mChannelCollection.getSupplicantScanFreqs());
+ assertNull(mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/NoBandChannelHelperTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/NoBandChannelHelperTest.java
index 2863b9f..145b214 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/NoBandChannelHelperTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/NoBandChannelHelperTest.java
@@ -221,7 +221,7 @@
assertThat(bucketSettings, channelsAre());
assertEquals(Collections.<Integer>emptySet(),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertTrue(mChannelCollection.isEmpty());
assertFalse(mChannelCollection.containsChannel(2400));
@@ -241,7 +241,7 @@
assertThat(bucketSettings, channelsAre());
assertEquals(Collections.<Integer>emptySet(),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertTrue(mChannelCollection.isEmpty());
assertFalse(mChannelCollection.containsChannel(2400));
@@ -259,7 +259,7 @@
mChannelCollection.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
assertThat(bucketSettings, bandIs(ALL_BANDS));
- assertNull(mChannelCollection.getSupplicantScanFreqs());
+ assertNull(mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -279,7 +279,7 @@
assertThat(bucketSettings, channelsAre(2400));
assertEquals(new HashSet<Integer>(Arrays.asList(2400)),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -300,7 +300,7 @@
assertThat(bucketSettings, channelsAre(2400, 2450));
assertEquals(new HashSet<Integer>(Arrays.asList(2400, 2450)),
- mChannelCollection.getSupplicantScanFreqs());
+ mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -320,7 +320,7 @@
mChannelCollection.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
assertThat(bucketSettings, bandIs(ALL_BANDS));
- assertNull(mChannelCollection.getSupplicantScanFreqs());
+ assertNull(mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -340,7 +340,7 @@
mChannelCollection.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
assertThat(bucketSettings, bandIs(ALL_BANDS));
- assertNull(mChannelCollection.getSupplicantScanFreqs());
+ assertNull(mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
@@ -360,7 +360,7 @@
mChannelCollection.fillBucketSettings(bucketSettings, Integer.MAX_VALUE);
assertThat(bucketSettings, bandIs(WifiScanner.WIFI_BAND_BOTH_WITH_DFS));
- assertNull(mChannelCollection.getSupplicantScanFreqs());
+ assertNull(mChannelCollection.getScanFreqs());
assertFalse(mChannelCollection.isEmpty());
assertTrue(mChannelCollection.containsChannel(2400));
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 3498b53..85b19fe 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java
@@ -17,13 +17,17 @@
package com.android.server.wifi.scanner;
import static com.android.server.wifi.ScanTestUtil.*;
+import static com.android.server.wifi.scanner.WifiScanningServiceImpl.WifiSingleScanStateMachine
+ .CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS;
import static org.junit.Assert.*;
-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.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
@@ -31,27 +35,28 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
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.FakeWifiLog;
+import com.android.server.wifi.FrameworkFacade;
import com.android.server.wifi.ScanResults;
import com.android.server.wifi.TestUtil;
import com.android.server.wifi.WifiInjector;
import com.android.server.wifi.WifiMetrics;
-import com.android.server.wifi.WifiMetricsProto;
import com.android.server.wifi.WifiNative;
+import com.android.server.wifi.nano.WifiMetricsProto;
+import com.android.server.wifi.util.WifiAsyncChannel;
import org.junit.After;
import org.junit.Before;
@@ -60,7 +65,8 @@
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.internal.matchers.CapturingMatcher;
+import org.mockito.Spy;
+import org.mockito.compat.CapturingMatcher;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -68,6 +74,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.regex.Pattern;
/**
@@ -77,15 +84,19 @@
public class WifiScanningServiceTest {
public static final String TAG = "WifiScanningServiceTest";
+ private static final int TEST_MAX_SCAN_BUCKETS_IN_CAPABILITIES = 8;
+
@Mock Context mContext;
- MockAlarmManager mAlarmManager;
+ TestAlarmManager mAlarmManager;
@Mock WifiScannerImpl mWifiScannerImpl;
@Mock WifiScannerImpl.WifiScannerImplFactory mWifiScannerImplFactory;
@Mock IBatteryStats mBatteryStats;
@Mock WifiInjector mWifiInjector;
+ @Mock FrameworkFacade mFrameworkFacade;
@Mock Clock mClock;
+ @Spy FakeWifiLog mLog;
WifiMetrics mWifiMetrics;
- MockLooper mLooper;
+ TestLooper mLooper;
WifiScanningServiceImpl mWifiScanningServiceImpl;
@@ -93,22 +104,28 @@
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);
ChannelHelper channelHelper = new PresetKnownBandsChannelHelper(
new int[]{2400, 2450},
new int[]{5150, 5175},
new int[]{5600, 5650, 5660});
- mLooper = new MockLooper();
+ mLooper = new TestLooper();
+ mWifiMetrics = new WifiMetrics(mClock, mLooper.getLooper());
when(mWifiScannerImplFactory
- .create(any(Context.class), any(Looper.class), any(Clock.class)))
+ .create(any(), any(), any()))
.thenReturn(mWifiScannerImpl);
when(mWifiScannerImpl.getChannelHelper()).thenReturn(channelHelper);
when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
+ when(mWifiInjector.makeLog(anyString())).thenReturn(mLog);
+ WifiAsyncChannel mWifiAsyncChannel = new WifiAsyncChannel("ScanningServiceTest");
+ mWifiAsyncChannel.setWifiLog(mLog);
+ when(mFrameworkFacade.makeWifiAsyncChannel(anyString())).thenReturn(mWifiAsyncChannel);
+ when(mWifiInjector.getFrameworkFacade()).thenReturn(mFrameworkFacade);
+ when(mWifiInjector.getClock()).thenReturn(mClock);
mWifiScanningServiceImpl = new WifiScanningServiceImpl(mContext, mLooper.getLooper(),
mWifiScannerImplFactory, mBatteryStats, mWifiInjector);
}
@@ -147,7 +164,7 @@
private static Message verifyHandleMessageAndGetMessage(InOrder order, Handler handler,
final int what) {
CapturingMatcher<Message> messageMatcher = new CapturingMatcher<Message>() {
- public boolean matches(Object argument) {
+ public boolean matchesObject(Object argument) {
Message message = (Message) argument;
return message.what == what;
}
@@ -156,7 +173,7 @@
return messageMatcher.getLastValue();
}
- private static void verifyScanResultsRecieved(InOrder order, Handler handler, int listenerId,
+ private static void verifyScanResultsReceived(InOrder order, Handler handler, int listenerId,
WifiScanner.ScanData... expected) {
Message scanResultMessage = verifyHandleMessageAndGetMessage(order, handler,
WifiScanner.CMD_SCAN_RESULT);
@@ -171,7 +188,7 @@
((WifiScanner.ParcelableScanData) scanResultMessage.obj).getResults());
}
- private static void verifySingleScanCompletedRecieved(InOrder order, Handler handler,
+ private static void verifySingleScanCompletedReceived(InOrder order, Handler handler,
int listenerId) {
Message completedMessage = verifyHandleMessageAndGetMessage(order, handler,
WifiScanner.CMD_SINGLE_SCAN_COMPLETED);
@@ -329,20 +346,18 @@
private static final int MAX_AP_PER_SCAN = 16;
private void startServiceAndLoadDriver() {
mWifiScanningServiceImpl.startService();
- setupAndLoadDriver();
+ setupAndLoadDriver(TEST_MAX_SCAN_BUCKETS_IN_CAPABILITIES);
}
- private void setupAndLoadDriver() {
+ private void setupAndLoadDriver(int max_scan_buckets) {
when(mWifiScannerImpl.getScanCapabilities(any(WifiNative.ScanCapabilities.class)))
.thenAnswer(new AnswerWithArguments() {
public boolean answer(WifiNative.ScanCapabilities capabilities) {
capabilities.max_scan_cache_size = Integer.MAX_VALUE;
- capabilities.max_scan_buckets = 8;
+ capabilities.max_scan_buckets = max_scan_buckets;
capabilities.max_ap_cache_per_scan = MAX_AP_PER_SCAN;
capabilities.max_rssi_sample_size = 8;
capabilities.max_scan_reporting_threshold = 10;
- capabilities.max_hotlist_bssids = 0;
- capabilities.max_significant_wifi_change_aps = 0;
return true;
}
});
@@ -394,6 +409,7 @@
@Test
public void startService() throws Exception {
mWifiScanningServiceImpl.startService();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
verifyNoMoreInteractions(mWifiScannerImplFactory);
Handler handler = mock(Handler.class);
@@ -407,7 +423,7 @@
@Test
public void disconnectClientBeforeWifiEnabled() throws Exception {
mWifiScanningServiceImpl.startService();
-
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
BidirectionalAsyncChannel controlChannel = connectChannel(mock(Handler.class));
mLooper.dispatchAll();
@@ -418,8 +434,9 @@
@Test
public void loadDriver() throws Exception {
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
verify(mWifiScannerImplFactory, times(1))
- .create(any(Context.class), any(Looper.class), any(Clock.class));
+ .create(any(), any(), any());
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
@@ -435,11 +452,11 @@
@Test
public void disconnectClientAfterStartingWifi() throws Exception {
mWifiScanningServiceImpl.startService();
-
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
BidirectionalAsyncChannel controlChannel = connectChannel(mock(Handler.class));
mLooper.dispatchAll();
- setupAndLoadDriver();
+ setupAndLoadDriver(TEST_MAX_SCAN_BUCKETS_IN_CAPABILITIES);
controlChannel.disconnect();
mLooper.dispatchAll();
@@ -448,6 +465,7 @@
@Test
public void connectAndDisconnectClientAfterStartingWifi() throws Exception {
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
BidirectionalAsyncChannel controlChannel = connectChannel(mock(Handler.class));
mLooper.dispatchAll();
@@ -458,6 +476,7 @@
@Test
public void sendInvalidCommand() throws Exception {
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
@@ -468,11 +487,27 @@
"Invalid request");
}
+ @Test
+ public void rejectBackgroundScanRequestWhenHalReturnsInvalidCapabilities() throws Exception {
+ mWifiScanningServiceImpl.startService();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
+
+ setupAndLoadDriver(0);
+
+ Handler handler = mock(Handler.class);
+ BidirectionalAsyncChannel controlChannel = connectChannel(handler);
+ InOrder order = inOrder(handler);
+ sendBackgroundScanRequest(controlChannel, 122, generateValidScanSettings(), null);
+ mLooper.dispatchAll();
+ verifyFailedResponse(order, handler, 122, WifiScanner.REASON_UNSPECIFIED, "not available");
+ }
+
private void doSuccessfulSingleScan(WifiScanner.ScanSettings requestSettings,
WifiNative.ScanSettings nativeSettings, ScanResults results) throws RemoteException {
int requestId = 12;
WorkSource workSource = new WorkSource(2292);
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
@@ -493,8 +528,13 @@
eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(order, handler, requestId, results.getScanData());
- verifySingleScanCompletedRecieved(order, handler, requestId);
+ verifyScanResultsReceived(order, handler, requestId, results.getScanData());
+ verifySingleScanCompletedReceived(order, handler, requestId);
+ if (results.getScanData().isAllChannelsScanned()) {
+ verify(mContext).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
+ } else {
+ verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
+ }
verifyNoMoreInteractions(handler);
verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource));
assertDumpContainsRequestLog("addSingleScanRequest", requestId);
@@ -574,6 +614,7 @@
int requestId = 33;
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
@@ -611,6 +652,7 @@
WorkSource workSource = new WorkSource(Binder.getCallingUid()); // don't explicitly set
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
@@ -639,6 +681,7 @@
assertEquals(mWifiMetrics.getOneshotScanCount(), 1);
assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_UNKNOWN), 1);
verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource));
+ verify(mContext).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
}
/**
@@ -651,6 +694,7 @@
int requestId = 2293;
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class),
any(WifiNative.ScanEventHandler.class))).thenReturn(true);
@@ -692,6 +736,7 @@
int listenerRequestId = 2295;
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class),
any(WifiNative.ScanEventHandler.class))).thenReturn(true);
@@ -739,23 +784,24 @@
WifiScanner.ScanSettings requestSettings1 = createRequest(channelsToSpec(2400), 0,
0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
int requestId1 = 12;
- ScanResults results1 = ScanResults.create(0, 2400);
+ ScanResults results1 = ScanResults.create(0, true, 2400);
WifiScanner.ScanSettings requestSettings2 = createRequest(channelsToSpec(2450, 5175), 0,
0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
int requestId2 = 13;
- ScanResults results2 = ScanResults.create(0, 2450);
+ ScanResults results2 = ScanResults.create(0, true, 2450);
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class),
any(WifiNative.ScanEventHandler.class))).thenReturn(true);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
- InOrder order = inOrder(handler, mWifiScannerImpl);
+ InOrder order = inOrder(handler, mWifiScannerImpl, mContext);
// Run scan 1
sendSingleScanRequest(controlChannel, requestId1, requestSettings1, null);
@@ -771,8 +817,15 @@
eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(order, handler, requestId1, results1.getScanData());
- verifySingleScanCompletedRecieved(order, handler, requestId1);
+ // Note: The order of the following verification calls looks out of order if you compare to
+ // the source code of WifiScanningServiceImpl WifiSingleScanStateMachine.reportScanResults.
+ // This is due to the fact that verifyScanResultsReceived and
+ // verifySingleScanCompletedReceived require an additional call to handle the message that
+ // is created in reportScanResults. This handling is done in the two verify*Received calls
+ // that is run AFTER the reportScanResults method in WifiScanningServiceImpl completes.
+ order.verify(mContext).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
+ verifyScanResultsReceived(order, handler, requestId1, results1.getScanData());
+ verifySingleScanCompletedReceived(order, handler, requestId1);
// Run scan 2
sendSingleScanRequest(controlChannel, requestId2, requestSettings2, null);
@@ -788,8 +841,9 @@
eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(order, handler, requestId2, results2.getScanData());
- verifySingleScanCompletedRecieved(order, handler, requestId2);
+ order.verify(mContext).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
+ verifyScanResultsReceived(order, handler, requestId2, results2.getScanData());
+ verifySingleScanCompletedReceived(order, handler, requestId2);
}
/**
@@ -801,15 +855,16 @@
WifiScanner.ScanSettings requestSettings1 = createRequest(channelsToSpec(2400), 0,
0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
int requestId1 = 12;
- ScanResults results1 = ScanResults.create(0, 2400);
+ ScanResults results1 = ScanResults.create(0, true, 2400);
WifiScanner.ScanSettings requestSettings2 = createRequest(channelsToSpec(2450, 5175), 0,
0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
int requestId2 = 13;
- ScanResults results2 = ScanResults.create(0, 2450);
+ ScanResults results2 = ScanResults.create(0, true, 2450);
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class),
any(WifiNative.ScanEventHandler.class))).thenReturn(true);
@@ -838,8 +893,9 @@
eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(handlerOrder, handler, requestId1, results1.getScanData());
- verifySingleScanCompletedRecieved(handlerOrder, handler, requestId1);
+ verifyScanResultsReceived(handlerOrder, handler, requestId1, results1.getScanData());
+ verifySingleScanCompletedReceived(handlerOrder, handler, requestId1);
+ verify(mContext).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
// now that the first scan completed we expect the second one to start
WifiNative.ScanEventHandler eventHandler2 = verifyStartSingleScan(nativeOrder,
@@ -851,8 +907,9 @@
eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(handlerOrder, handler, requestId2, results2.getScanData());
- verifySingleScanCompletedRecieved(handlerOrder, handler, requestId2);
+ verifyScanResultsReceived(handlerOrder, handler, requestId2, results2.getScanData());
+ verifySingleScanCompletedReceived(handlerOrder, handler, requestId2);
+ verify(mContext, times(2)).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
assertEquals(mWifiMetrics.getOneshotScanCount(), 2);
assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_SUCCESS), 2);
}
@@ -868,19 +925,19 @@
0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
int requestId1 = 12;
WorkSource workSource1 = new WorkSource(1121);
- ScanResults results1 = ScanResults.create(0, 2400);
+ ScanResults results1 = ScanResults.create(0, false, 2400);
WifiScanner.ScanSettings requestSettings2 = createRequest(channelsToSpec(2450, 5175), 0,
0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
int requestId2 = 13;
WorkSource workSource2 = new WorkSource(Binder.getCallingUid()); // don't explicitly set
- ScanResults results2 = ScanResults.create(0, 2450, 5175, 2450);
+ ScanResults results2 = ScanResults.create(0, false, 2450, 5175, 2450);
WifiScanner.ScanSettings requestSettings3 = createRequest(channelsToSpec(5150), 0,
0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
int requestId3 = 15;
WorkSource workSource3 = new WorkSource(2292);
- ScanResults results3 = ScanResults.create(0, 5150, 5150, 5150, 5150);
+ ScanResults results3 = ScanResults.create(0, false, 5150, 5150, 5150, 5150);
WifiNative.ScanSettings nativeSettings2and3 = createSingleScanNativeSettingsForChannels(
WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN, channelsToSpec(2450, 5175, 5150));
@@ -891,6 +948,7 @@
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class),
any(WifiNative.ScanEventHandler.class))).thenReturn(true);
@@ -927,8 +985,9 @@
eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(handlerOrder, handler, requestId1, results1.getScanData());
- verifySingleScanCompletedRecieved(handlerOrder, handler, requestId1);
+ verifyScanResultsReceived(handlerOrder, handler, requestId1, results1.getScanData());
+ verifySingleScanCompletedReceived(handlerOrder, handler, requestId1);
+ verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
verify(mBatteryStats).noteWifiScanStoppedFromSource(eq(workSource1));
verify(mBatteryStats).noteWifiScanStartedFromSource(eq(workSource2and3));
@@ -985,6 +1044,7 @@
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class),
any(WifiNative.ScanEventHandler.class))).thenReturn(true);
@@ -1052,6 +1112,7 @@
ScanResults results3 = results2400;
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class),
any(WifiNative.ScanEventHandler.class))).thenReturn(true);
@@ -1105,8 +1166,9 @@
mLooper.dispatchAll();
- verifyScanResultsRecieved(handlerOrder, handler, requestId2, results2.getScanData());
- verifySingleScanCompletedRecieved(handlerOrder, handler, requestId2);
+ verifyScanResultsReceived(handlerOrder, handler, requestId2, results2.getScanData());
+ verifySingleScanCompletedReceived(handlerOrder, handler, requestId2);
+ verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
assertEquals(mWifiMetrics.getOneshotScanCount(), 3);
assertEquals(mWifiMetrics.getScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_SUCCESS), 3);
@@ -1124,6 +1186,278 @@
}
/**
+ * Verify that WifiService provides a way to get the most recent SingleScan results.
+ */
+ @Test
+ public void retrieveSingleScanResults() throws Exception {
+ WifiScanner.ScanSettings requestSettings =
+ createRequest(WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
+ 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
+ ScanResults expectedResults = ScanResults.create(0, true, 2400, 5150, 5175);
+ doSuccessfulSingleScan(requestSettings,
+ computeSingleScanNativeSettings(requestSettings),
+ expectedResults);
+ Handler handler = mock(Handler.class);
+ BidirectionalAsyncChannel controlChannel = connectChannel(handler);
+ InOrder order = inOrder(handler, mWifiScannerImpl);
+
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response = verifyHandleMessageAndGetMessage(order, handler);
+ List<ScanResult> results = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response.obj).getResults());
+ assertEquals(results.size(), expectedResults.getRawScanResults().length);
+
+ // Make sure that we logged the scan results in the dump method.
+ String serviceDump = dumpService();
+ Pattern logLineRegex = Pattern.compile("Latest scan results:");
+ assertTrue("dump did not contain Latest scan results: " + serviceDump + "\n",
+ logLineRegex.matcher(serviceDump).find());
+ }
+
+ /**
+ * Verify that WifiService provides a way to get the most recent SingleScan results even when
+ * they are empty.
+ */
+ @Test
+ public void retrieveSingleScanResultsEmpty() throws Exception {
+ WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0,
+ 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
+ doSuccessfulSingleScan(requestSettings, computeSingleScanNativeSettings(requestSettings),
+ ScanResults.create(0, new int[0]));
+
+ Handler handler = mock(Handler.class);
+ BidirectionalAsyncChannel controlChannel = connectChannel(handler);
+ InOrder order = inOrder(handler, mWifiScannerImpl);
+
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response = verifyHandleMessageAndGetMessage(order, handler);
+
+ List<ScanResult> results = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response.obj).getResults());
+ assertEquals(results.size(), 0);
+ }
+
+ /**
+ * Verify that WifiService will return empty SingleScan results if a scan has not been
+ * performed.
+ */
+ @Test
+ public void retrieveSingleScanResultsBeforeAnySingleScans() throws Exception {
+ startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
+ Handler handler = mock(Handler.class);
+ BidirectionalAsyncChannel controlChannel = connectChannel(handler);
+ InOrder order = inOrder(handler, mWifiScannerImpl);
+
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response = verifyHandleMessageAndGetMessage(order, handler);
+
+ List<ScanResult> results = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response.obj).getResults());
+ assertEquals(results.size(), 0);
+ }
+
+ /**
+ * Verify that the newest full scan results are returned by WifiService.getSingleScanResults.
+ */
+ @Test
+ public void retrieveMostRecentFullSingleScanResults() throws Exception {
+ WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0,
+ 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
+ ScanResults expectedResults = ScanResults.create(0, true, 2400, 5150, 5175);
+ doSuccessfulSingleScan(requestSettings,
+ computeSingleScanNativeSettings(requestSettings),
+ expectedResults);
+
+ Handler handler = mock(Handler.class);
+ BidirectionalAsyncChannel controlChannel = connectChannel(handler);
+ InOrder order = inOrder(handler, mWifiScannerImpl);
+
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response = verifyHandleMessageAndGetMessage(order, handler);
+
+ List<ScanResult> results = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response.obj).getResults());
+ assertEquals(results.size(), expectedResults.getRawScanResults().length);
+
+ // now update with a new scan that only has one result
+ int secondScanRequestId = 35;
+ ScanResults expectedSingleResult = ScanResults.create(0, true, 5150);
+ sendSingleScanRequest(controlChannel, secondScanRequestId, requestSettings, null);
+
+ mLooper.dispatchAll();
+ WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(order,
+ computeSingleScanNativeSettings(requestSettings));
+ verifySuccessfulResponse(order, handler, secondScanRequestId);
+
+ // dispatch scan 2 results
+ when(mWifiScannerImpl.getLatestSingleScanResults())
+ .thenReturn(expectedSingleResult.getScanData());
+ eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
+
+ mLooper.dispatchAll();
+ verifyScanResultsReceived(order, handler, secondScanRequestId,
+ expectedSingleResult.getScanData());
+ verifySingleScanCompletedReceived(order, handler, secondScanRequestId);
+
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response2 = verifyHandleMessageAndGetMessage(order, handler);
+
+ List<ScanResult> results2 = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response2.obj).getResults());
+ assertEquals(results2.size(), expectedSingleResult.getRawScanResults().length);
+ }
+
+ /**
+ * Verify that the newest partial scan results are not returned by
+ * WifiService.getSingleScanResults.
+ */
+ @Test
+ public void doesNotRetrieveMostRecentPartialSingleScanResults() throws Exception {
+ WifiScanner.ScanSettings fullRequestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0,
+ 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
+ ScanResults expectedFullResults = ScanResults.create(0, true, 2400, 5150, 5175);
+ doSuccessfulSingleScan(fullRequestSettings,
+ computeSingleScanNativeSettings(fullRequestSettings),
+ expectedFullResults);
+
+ Handler handler = mock(Handler.class);
+ BidirectionalAsyncChannel controlChannel = connectChannel(handler);
+ InOrder order = inOrder(handler, mWifiScannerImpl);
+
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response = verifyHandleMessageAndGetMessage(order, handler);
+
+ List<ScanResult> results = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response.obj).getResults());
+ assertEquals(results.size(), expectedFullResults.getRawScanResults().length);
+
+ // now update with a new scan that only has one result
+ int secondScanRequestId = 35;
+ WifiScanner.ScanSettings partialRequestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH,
+ 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
+ ScanResults expectedPartialResults = ScanResults.create(0, false, 5150);
+ sendSingleScanRequest(controlChannel, secondScanRequestId, partialRequestSettings, null);
+
+ mLooper.dispatchAll();
+ WifiNative.ScanEventHandler eventHandler = verifyStartSingleScan(order,
+ computeSingleScanNativeSettings(partialRequestSettings));
+ verifySuccessfulResponse(order, handler, secondScanRequestId);
+
+ // dispatch scan 2 results
+ when(mWifiScannerImpl.getLatestSingleScanResults())
+ .thenReturn(expectedPartialResults.getScanData());
+ eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
+
+ mLooper.dispatchAll();
+ verifyScanResultsReceived(order, handler, secondScanRequestId,
+ expectedPartialResults.getScanData());
+ verifySingleScanCompletedReceived(order, handler, secondScanRequestId);
+
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response2 = verifyHandleMessageAndGetMessage(order, handler);
+
+ List<ScanResult> results2 = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response2.obj).getResults());
+ assertEquals(results2.size(), expectedFullResults.getRawScanResults().length);
+ }
+
+ /**
+ * Verify that the scan results returned by WifiService.getSingleScanResults are not older
+ * than {@link com.android.server.wifi.scanner.WifiScanningServiceImpl
+ * .WifiSingleScanStateMachine#CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS}.
+ */
+ @Test
+ public void doesNotRetrieveStaleScanResultsFromLastFullSingleScan() throws Exception {
+ WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0,
+ 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
+ ScanResults scanResults = ScanResults.create(0, true, 2400, 5150, 5175);
+
+ // Out of the 3 scan results, modify the timestamp of 2 of them to be within the expiration
+ // age and 1 out of it.
+ long currentTimeInMillis = CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS * 2;
+ when(mClock.getElapsedSinceBootMillis()).thenReturn(currentTimeInMillis);
+ scanResults.getRawScanResults()[0].timestamp = (currentTimeInMillis - 1) * 1000;
+ scanResults.getRawScanResults()[1].timestamp = (currentTimeInMillis - 2) * 1000;
+ scanResults.getRawScanResults()[2].timestamp =
+ (currentTimeInMillis - CACHED_SCAN_RESULTS_MAX_AGE_IN_MILLIS) * 1000;
+ List<ScanResult> expectedResults = new ArrayList<ScanResult>() {{
+ add(scanResults.getRawScanResults()[0]);
+ add(scanResults.getRawScanResults()[1]);
+ }};
+
+ doSuccessfulSingleScan(requestSettings,
+ computeSingleScanNativeSettings(requestSettings), scanResults);
+
+ Handler handler = mock(Handler.class);
+ BidirectionalAsyncChannel controlChannel = connectChannel(handler);
+ InOrder order = inOrder(handler, mWifiScannerImpl);
+
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response = verifyHandleMessageAndGetMessage(order, handler);
+
+ List<ScanResult> results = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response.obj).getResults());
+ assertScanResultsEquals(expectedResults.toArray(new ScanResult[expectedResults.size()]),
+ results.toArray(new ScanResult[results.size()]));
+ }
+
+ /**
+ * Cached scan results should be cleared after the driver is unloaded.
+ */
+ @Test
+ public void validateScanResultsClearedAfterDriverUnloaded() throws Exception {
+ WifiScanner.ScanSettings requestSettings =
+ createRequest(WifiScanner.WIFI_BAND_BOTH_WITH_DFS,
+ 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
+ ScanResults expectedResults = ScanResults.create(0, true, 2400, 5150, 5175);
+ doSuccessfulSingleScan(requestSettings,
+ computeSingleScanNativeSettings(requestSettings),
+ expectedResults);
+ Handler handler = mock(Handler.class);
+ BidirectionalAsyncChannel controlChannel = connectChannel(handler);
+ InOrder order = inOrder(handler, mWifiScannerImpl);
+
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response = verifyHandleMessageAndGetMessage(order, handler);
+ List<ScanResult> results = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response.obj).getResults());
+ assertEquals(results.size(), expectedResults.getRawScanResults().length);
+
+ // disable wifi
+ TestUtil.sendWifiScanAvailable(mBroadcastReceiver,
+ mContext,
+ WifiManager.WIFI_STATE_DISABLED);
+ // Now get scan results again. The returned list should be empty since we
+ // clear the cache when exiting the DriverLoaded state.
+ controlChannel.sendMessage(
+ Message.obtain(null, WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS, 0));
+ mLooper.dispatchAll();
+ Message response2 = verifyHandleMessageAndGetMessage(order, handler);
+ List<ScanResult> results2 = Arrays.asList(
+ ((WifiScanner.ParcelableScanResults) response2.obj).getResults());
+ assertEquals(results2.size(), 0);
+ }
+
+ /**
* Register a single scan listener and do a single scan
*/
@Test
@@ -1137,6 +1471,7 @@
int listenerRequestId = 13;
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
@@ -1160,9 +1495,10 @@
eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(order, handler, requestId, results.getScanData());
- verifySingleScanCompletedRecieved(order, handler, requestId);
- verifyScanResultsRecieved(order, handler, listenerRequestId, results.getScanData());
+ verifyScanResultsReceived(order, handler, requestId, results.getScanData());
+ verifySingleScanCompletedReceived(order, handler, requestId);
+ verifyScanResultsReceived(order, handler, listenerRequestId, results.getScanData());
+ verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
verifyNoMoreInteractions(handler);
assertDumpContainsRequestLog("registerScanListener", listenerRequestId);
@@ -1184,6 +1520,7 @@
int listenerRequestId = 13;
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
@@ -1210,8 +1547,9 @@
eventHandler.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(order, handler, requestId, results.getScanData());
- verifySingleScanCompletedRecieved(order, handler, requestId);
+ verifyScanResultsReceived(order, handler, requestId, results.getScanData());
+ verifySingleScanCompletedReceived(order, handler, requestId);
+ verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
verifyNoMoreInteractions(handler);
assertDumpContainsRequestLog("registerScanListener", listenerRequestId);
@@ -1223,7 +1561,7 @@
* satisfied by the first scan. Verify that the first completes and the second two are merged.
*/
@Test
- public void scanListenerRecievesAllResults() throws RemoteException {
+ public void scanListenerReceivesAllResults() throws RemoteException {
WifiScanner.ScanSettings requestSettings1 = createRequest(channelsToSpec(2400), 0,
0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN);
int requestId1 = 12;
@@ -1247,6 +1585,7 @@
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
when(mWifiScannerImpl.startSingleScan(any(WifiNative.ScanSettings.class),
any(WifiNative.ScanEventHandler.class))).thenReturn(true);
@@ -1286,9 +1625,10 @@
eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(handlerOrder, handler, requestId1, results1.getScanData());
- verifySingleScanCompletedRecieved(handlerOrder, handler, requestId1);
- verifyScanResultsRecieved(handlerOrder, handler, listenerRequestId, results1.getScanData());
+ verifyScanResultsReceived(handlerOrder, handler, requestId1, results1.getScanData());
+ verifySingleScanCompletedReceived(handlerOrder, handler, requestId1);
+ verifyScanResultsReceived(handlerOrder, handler, listenerRequestId, results1.getScanData());
+ verify(mContext, never()).sendBroadcastAsUser(any(Intent.class), eq(UserHandle.ALL));
// now that the first scan completed we expect the second and third ones to start
WifiNative.ScanEventHandler eventHandler2and3 = verifyStartSingleScan(nativeOrder,
@@ -1315,6 +1655,7 @@
private void doSuccessfulBackgroundScan(WifiScanner.ScanSettings requestSettings,
WifiNative.ScanSettings nativeSettings) {
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
@@ -1421,9 +1762,6 @@
for (i = 0; i < requestPnoSettings.networkList.length; i++) {
nativePnoSettings.networkList[i] = new WifiNative.PnoNetwork();
nativePnoSettings.networkList[i].ssid = requestPnoSettings.networkList[i].ssid;
- nativePnoSettings.networkList[i].networkId =
- requestPnoSettings.networkList[i].networkId;
- nativePnoSettings.networkList[i].priority = requestPnoSettings.networkList[i].priority;
nativePnoSettings.networkList[i].flags = requestPnoSettings.networkList[i].flags;
nativePnoSettings.networkList[i].auth_bit_field =
requestPnoSettings.networkList[i].authBitField;
@@ -1470,7 +1808,7 @@
((WifiScanner.ParcelableScanResults) networkFoundMessage.obj).getResults());
}
- private void verifyPnoNetworkFoundRecieved(InOrder order, Handler handler, int listenerId,
+ private void verifyPnoNetworkFoundReceived(InOrder order, Handler handler, int listenerId,
ScanResult[] expected) {
Message scanResultMessage = verifyHandleMessageAndGetMessage(order, handler,
WifiScanner.CMD_PNO_NETWORK_FOUND);
@@ -1549,12 +1887,13 @@
}
/**
- * Tests Supplicant PNO scan when the PNO scan results contain IE info. This ensures that the
+ * Tests wificond PNO scan when the PNO scan results contain IE info. This ensures that the
* PNO scan results are plumbed back to the client as a PNO network found event.
*/
@Test
public void testSuccessfulHwPnoScanWithNoBackgroundScan() throws Exception {
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
InOrder order = inOrder(handler, mWifiScannerImpl);
@@ -1569,7 +1908,7 @@
sendPnoScanRequest(controlChannel, requestId, scanSettings.first, pnoSettings.first);
expectHwPnoScanWithNoBackgroundScan(order, handler, requestId, pnoSettings.second,
scanResults);
- verifyPnoNetworkFoundRecieved(order, handler, requestId, scanResults.getRawScanResults());
+ verifyPnoNetworkFoundReceived(order, handler, requestId, scanResults.getRawScanResults());
}
/**
@@ -1579,6 +1918,7 @@
@Test
public void testSuccessfulHwPnoScanWithBackgroundScan() throws Exception {
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
InOrder order = inOrder(handler, mWifiScannerImpl);
@@ -1593,7 +1933,7 @@
sendPnoScanRequest(controlChannel, requestId, scanSettings.first, pnoSettings.first);
expectHwPnoScanWithBackgroundScan(order, handler, requestId, scanSettings.second,
pnoSettings.second, scanResults);
- verifyPnoNetworkFoundRecieved(order, handler, requestId, scanResults.getRawScanResults());
+ verifyPnoNetworkFoundReceived(order, handler, requestId, scanResults.getRawScanResults());
}
/**
@@ -1603,6 +1943,7 @@
@Test
public void testSuccessfulHwPnoScanWithBackgroundScanWithNoIE() throws Exception {
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
InOrder order = inOrder(handler, mWifiScannerImpl);
@@ -1622,7 +1963,7 @@
ArrayList<ScanResult> sortScanList =
new ArrayList<ScanResult>(Arrays.asList(scanResults.getRawScanResults()));
Collections.sort(sortScanList, WifiScannerImpl.SCAN_RESULT_SORT_COMPARATOR);
- verifyPnoNetworkFoundRecieved(order, handler, requestId,
+ verifyPnoNetworkFoundReceived(order, handler, requestId,
sortScanList.toArray(new ScanResult[sortScanList.size()]));
}
@@ -1633,6 +1974,7 @@
@Test
public void testSuccessfulSwPnoScan() throws Exception {
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
InOrder order = inOrder(handler, mWifiScannerImpl);
@@ -1646,7 +1988,7 @@
sendPnoScanRequest(controlChannel, requestId, scanSettings.first, pnoSettings.first);
expectSwPnoScan(order, scanSettings.second, scanResults);
- verifyPnoNetworkFoundRecieved(order, handler, requestId, scanResults.getRawScanResults());
+ verifyPnoNetworkFoundReceived(order, handler, requestId, scanResults.getRawScanResults());
}
/**
@@ -1656,6 +1998,7 @@
@Test
public void processSingleScanRequestAfterDisconnect() throws Exception {
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
BidirectionalAsyncChannel controlChannel = connectChannel(mock(Handler.class));
mLooper.dispatchAll();
@@ -1694,6 +2037,7 @@
int requestId = 9;
startServiceAndLoadDriver();
+ mWifiScanningServiceImpl.setWifiHandlerLogForTest(mLog);
Handler handler = mock(Handler.class);
BidirectionalAsyncChannel controlChannel = connectChannel(handler);
mLooper.dispatchAll();
@@ -1714,8 +2058,8 @@
eventHandler1.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(order, handler, requestId, results.getScanData());
- verifySingleScanCompletedRecieved(order, handler, requestId);
+ verifyScanResultsReceived(order, handler, requestId, results.getScanData());
+ verifySingleScanCompletedReceived(order, handler, requestId);
verifyNoMoreInteractions(handler);
controlChannel.sendMessage(Message.obtain(null, AsyncChannel.CMD_CHANNEL_DISCONNECTED,
@@ -1730,8 +2074,8 @@
eventHandler2.onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
mLooper.dispatchAll();
- verifyScanResultsRecieved(order, handler, requestId, results.getScanData());
- verifySingleScanCompletedRecieved(order, handler, requestId);
+ verifyScanResultsReceived(order, handler, requestId, results.getScanData());
+ verifySingleScanCompletedReceived(order, handler, requestId);
verifyNoMoreInteractions(handler);
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java
similarity index 83%
rename from tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java
rename to tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java
index 560710f..c63a9a0 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantPnoScannerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WificondPnoScannerTest.java
@@ -22,16 +22,15 @@
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.net.wifi.WifiScanner;
import android.os.SystemClock;
+import android.os.test.TestLooper;
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;
@@ -46,31 +45,29 @@
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.Set;
-
/**
- * Unit tests for {@link com.android.server.wifi.scanner.SupplicantWifiScannerImpl.setPnoList}.
+ * Unit tests for {@link com.android.server.wifi.scanner.WificondScannerImpl.setPnoList}.
*/
@SmallTest
-public class SupplicantPnoScannerTest {
+public class WificondPnoScannerTest {
@Mock Context mContext;
- MockAlarmManager mAlarmManager;
+ TestAlarmManager mAlarmManager;
MockWifiMonitor mWifiMonitor;
- MockLooper mLooper;
+ TestLooper mLooper;
@Mock WifiNative mWifiNative;
MockResources mResources;
@Mock Clock mClock;
- SupplicantWifiScannerImpl mScanner;
+ WificondScannerImpl mScanner;
@Before
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();
@@ -78,11 +75,11 @@
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());
}
/**
- * Verify that the HW disconnected PNO scan triggers a supplicant PNO scan and invokes the
+ * Verify that the HW disconnected PNO scan triggers a wificond PNO scan and invokes the
* OnPnoNetworkFound callback when the scan results are received.
*/
@Test
@@ -121,8 +118,7 @@
assertTrue(mScanner.startSingleScan(settings, eventHandler));
// Verify that the PNO scan was paused and single scan runs successfully
expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler,
- expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>(),
- scanResults);
+ expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), scanResults);
verifyNoMoreInteractions(eventHandler);
order = inOrder(pnoEventHandler, mWifiNative);
@@ -130,7 +126,7 @@
// alarm fires.
assertTrue("dispatch pno monitor alarm",
mAlarmManager.dispatch(
- SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
+ WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
// Now verify that PNO scan is resumed successfully
expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
@@ -191,16 +187,15 @@
assertTrue(mScanner.startSingleScan(settings, eventHandler));
// Verify that the PNO scan was paused and single scan runs successfully
expectSuccessfulSingleScanWithHwPnoEnabled(order, eventHandler,
- expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>(),
- scanResults);
+ expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), scanResults);
verifyNoMoreInteractions(eventHandler);
// Fail the PNO resume and check that the OnPnoScanFailed callback is invoked.
order = inOrder(pnoEventHandler, mWifiNative);
- when(mWifiNative.setPnoScan(true)).thenReturn(false);
+ when(mWifiNative.startPnoScan(any(WifiNative.PnoSettings.class))).thenReturn(false);
assertTrue("dispatch pno monitor alarm",
mAlarmManager.dispatch(
- SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
+ WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
order.verify(pnoEventHandler).onPnoScanFailed();
verifyNoMoreInteractions(pnoEventHandler);
@@ -209,7 +204,7 @@
startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
assertTrue("dispatch pno monitor alarm",
mAlarmManager.dispatch(
- SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
+ WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
assertEquals("dispatch message after alarm", 1, mLooper.dispatchAll());
expectSuccessfulHwDisconnectedPnoScan(order, pnoSettings, pnoEventHandler, scanResults);
verifyNoMoreInteractions(pnoEventHandler);
@@ -232,19 +227,19 @@
startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
// Fail the PNO stop.
- when(mWifiNative.setPnoScan(false)).thenReturn(false);
+ when(mWifiNative.stopPnoScan()).thenReturn(false);
assertTrue(mScanner.resetHwPnoList());
assertTrue("dispatch pno monitor alarm",
mAlarmManager.dispatch(
- SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
+ WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
mLooper.dispatchAll();
- verify(mWifiNative).setPnoScan(false);
+ verify(mWifiNative).stopPnoScan();
// Add a new PNO scan request and ensure it runs successfully.
startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
assertTrue("dispatch pno monitor alarm",
mAlarmManager.dispatch(
- SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
+ WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
mLooper.dispatchAll();
InOrder order = inOrder(pnoEventHandler, mWifiNative);
ScanResults scanResults = createDummyScanResults(false);
@@ -275,24 +270,24 @@
// Stop PNO now. This should trigger the debounce timer and not stop PNO.
assertTrue(mScanner.resetHwPnoList());
assertTrue(mAlarmManager.isPending(
- SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
- order.verify(mWifiNative, never()).setPnoScan(false);
+ WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
+ order.verify(mWifiNative, never()).stopPnoScan();
// Now restart PNO scan with an extra network in settings.
pnoSettings.networkList =
Arrays.copyOf(pnoSettings.networkList, pnoSettings.networkList.length + 1);
pnoSettings.networkList[pnoSettings.networkList.length - 1] =
- createDummyPnoNetwork("ssid_pno_new", 6, 6);
+ createDummyPnoNetwork("ssid_pno_new");
startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
// This should bypass the debounce timer and stop PNO scan immediately and then start
// a new debounce timer for the start.
- order.verify(mWifiNative).setPnoScan(false);
+ order.verify(mWifiNative).stopPnoScan();
// Trigger the debounce timer and ensure we start PNO scan again.
- mAlarmManager.dispatch(SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG);
+ mAlarmManager.dispatch(WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG);
mLooper.dispatchAll();
- order.verify(mWifiNative).setPnoScan(true);
+ order.verify(mWifiNative).startPnoScan(pnoSettings);
}
/**
@@ -318,15 +313,16 @@
// Stop PNO now. This should trigger the debounce timer and not stop PNO.
assertTrue(mScanner.resetHwPnoList());
assertTrue(mAlarmManager.isPending(
- SupplicantWifiScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
- order.verify(mWifiNative, never()).setPnoScan(false);
+ WificondScannerImpl.HwPnoDebouncer.PNO_DEBOUNCER_ALARM_TAG));
+ order.verify(mWifiNative, never()).stopPnoScan();
// Now restart PNO scan with the same settings.
startSuccessfulPnoScan(null, pnoSettings, null, pnoEventHandler);
// Trigger the debounce timer and ensure that we neither stop/start.
mLooper.dispatchAll();
- order.verify(mWifiNative, never()).setPnoScan(anyBoolean());
+ order.verify(mWifiNative, never()).startPnoScan(any(WifiNative.PnoSettings.class));
+ order.verify(mWifiNative, never()).stopPnoScan();
}
private void doSuccessfulSwPnoScanTest(boolean isConnectedPno) {
@@ -348,21 +344,19 @@
private void createScannerWithHwPnoScanSupport() {
mResources.setBoolean(R.bool.config_wifi_background_scan_support, true);
- mScanner =
- new SupplicantWifiScannerImpl(mContext, mWifiNative, mLooper.getLooper(), mClock);
+ mScanner = new WificondScannerImpl(mContext, mWifiNative, mWifiMonitor,
+ mLooper.getLooper(), mClock);
}
private void createScannerWithSwPnoScanSupport() {
mResources.setBoolean(R.bool.config_wifi_background_scan_support, false);
- mScanner =
- new SupplicantWifiScannerImpl(mContext, mWifiNative, mLooper.getLooper(), mClock);
+ mScanner = new WificondScannerImpl(mContext, mWifiNative, mWifiMonitor,
+ mLooper.getLooper(), mClock);
}
- private WifiNative.PnoNetwork createDummyPnoNetwork(String ssid, int networkId, int priority) {
+ private WifiNative.PnoNetwork createDummyPnoNetwork(String ssid) {
WifiNative.PnoNetwork pnoNetwork = new WifiNative.PnoNetwork();
pnoNetwork.ssid = ssid;
- pnoNetwork.networkId = networkId;
- pnoNetwork.priority = priority;
return pnoNetwork;
}
@@ -370,8 +364,8 @@
WifiNative.PnoSettings pnoSettings = new WifiNative.PnoSettings();
pnoSettings.isConnected = isConnected;
pnoSettings.networkList = new WifiNative.PnoNetwork[2];
- pnoSettings.networkList[0] = createDummyPnoNetwork("ssid_pno_1", 1, 1);
- pnoSettings.networkList[1] = createDummyPnoNetwork("ssid_pno_2", 2, 2);
+ pnoSettings.networkList[0] = createDummyPnoNetwork("ssid_pno_1");
+ pnoSettings.networkList[1] = createDummyPnoNetwork("ssid_pno_2");
return pnoSettings;
}
@@ -394,11 +388,10 @@
WifiNative.PnoSettings pnoSettings, WifiNative.ScanEventHandler scanEventHandler,
WifiNative.PnoEventHandler pnoEventHandler) {
reset(mWifiNative);
- when(mWifiNative.setNetworkVariable(anyInt(), anyString(), anyString())).thenReturn(true);
- when(mWifiNative.enableNetworkWithoutConnect(anyInt())).thenReturn(true);
// Scans succeed
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
- when(mWifiNative.setPnoScan(anyBoolean())).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
+ when(mWifiNative.startPnoScan(any(WifiNative.PnoSettings.class))).thenReturn(true);
+ when(mWifiNative.stopPnoScan()).thenReturn(true);
if (mScanner.isHwPnoSupported(pnoSettings.isConnected)) {
// This should happen only for HW PNO scan
@@ -413,7 +406,7 @@
private Set<Integer> expectedBandScanFreqs(int band) {
ChannelCollection collection = mScanner.getChannelHelper().createChannelCollection();
collection.addBand(band);
- return collection.getSupplicantScanFreqs();
+ return collection.getScanFreqs();
}
/**
@@ -421,14 +414,8 @@
*/
private void expectHwDisconnectedPnoScanStart(InOrder order,
WifiNative.PnoSettings pnoSettings) {
- for (int i = 0; i < pnoSettings.networkList.length; i++) {
- WifiNative.PnoNetwork network = pnoSettings.networkList[i];
- order.verify(mWifiNative).setNetworkVariable(network.networkId,
- WifiConfiguration.priorityVarName, Integer.toString(network.priority));
- order.verify(mWifiNative).enableNetworkWithoutConnect(network.networkId);
- }
// Verify HW PNO scan started
- order.verify(mWifiNative).setPnoScan(true);
+ order.verify(mWifiNative).startPnoScan(any(WifiNative.PnoSettings.class));
}
/**
@@ -445,7 +432,8 @@
when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList());
// Notify scan has finished
- mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_RESULTS_EVENT);
+ mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(),
+ WifiMonitor.PNO_SCAN_RESULTS_EVENT);
assertEquals("dispatch message after results event", 1, mLooper.dispatchAll());
order.verify(eventHandler).onPnoNetworkFound(scanResults.getRawScanResults());
@@ -457,11 +445,11 @@
*/
private void expectSuccessfulSingleScanWithHwPnoEnabled(InOrder order,
WifiNative.ScanEventHandler eventHandler, Set<Integer> expectedScanFreqs,
- Set<Integer> expectedHiddenNetIds, ScanResults scanResults) {
+ ScanResults scanResults) {
// Pause PNO scan first
- order.verify(mWifiNative).setPnoScan(false);
+ order.verify(mWifiNative).stopPnoScan();
- order.verify(mWifiNative).scan(eq(expectedScanFreqs), eq(expectedHiddenNetIds));
+ order.verify(mWifiNative).scan(eq(expectedScanFreqs), any(Set.class));
when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList());
@@ -483,10 +471,10 @@
WifiNative.ScanEventHandler eventHandler, ScanResults scanResults) {
// Verify scan started
- order.verify(mWifiNative).scan(any(Set.class), any(Set.class));
+ order.verify(mWifiNative).scan(any(), any(Set.class));
// Make sure that HW PNO scan was not started
- verify(mWifiNative, never()).setPnoScan(anyBoolean());
+ verify(mWifiNative, never()).startPnoScan(any(WifiNative.PnoSettings.class));
// Setup scan results
when(mWifiNative.getScanResults()).thenReturn(scanResults.getScanDetailArrayList());
@@ -498,7 +486,7 @@
// Verify background scan results delivered
order.verify(eventHandler).onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE);
WifiScanner.ScanData[] scanData = mScanner.getLatestBatchedScanResults(true);
- WifiScanner.ScanData lastScanData = scanData[scanData.length -1];
+ WifiScanner.ScanData lastScanData = scanData[scanData.length - 1];
assertScanDataEquals(scanResults.getScanData(), lastScanData);
}
}
diff --git a/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantWifiScannerTest.java b/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java
similarity index 93%
rename from tests/wifitests/src/com/android/server/wifi/scanner/SupplicantWifiScannerTest.java
rename to tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java
index 7bf5481..3a5f6b9 100644
--- a/tests/wifitests/src/com/android/server/wifi/scanner/SupplicantWifiScannerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/scanner/WificondScannerTest.java
@@ -38,18 +38,17 @@
import org.mockito.InOrder;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.Set;
/**
- * Unit tests for {@link com.android.server.wifi.scanner.SupplicantWifiScannerImpl}.
+ * Unit tests for {@link com.android.server.wifi.scanner.WificondScannerImpl}.
*/
@SmallTest
-public class SupplicantWifiScannerTest extends BaseWifiScannerImplTest {
+public class WificondScannerTest extends BaseWifiScannerImplTest {
@Before
public void setup() throws Exception {
- mScanner = new SupplicantWifiScannerImpl(mContext, mWifiNative,
+ mScanner = new WificondScannerImpl(mContext, mWifiNative, mWifiMonitor,
mLooper.getLooper(), mClock);
}
@@ -319,7 +318,7 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// All scans fail
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(false);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(false);
// Start scan
mScanner.startBatchedScan(settings, eventHandler);
@@ -327,7 +326,7 @@
assertBackgroundPeriodAlarmPending();
expectFailedScanStart(order, eventHandler,
- expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>());
+ expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ));
// Fire alarm to start next scan
dispatchBackgroundPeriodAlarm();
@@ -335,7 +334,7 @@
assertBackgroundPeriodAlarmPending();
expectFailedScanStart(order, eventHandler,
- expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>());
+ expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ));
verifyNoMoreInteractions(eventHandler);
}
@@ -354,7 +353,7 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// All scan starts succeed
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// Start scan
mScanner.startBatchedScan(settings, eventHandler);
@@ -362,7 +361,7 @@
assertBackgroundPeriodAlarmPending();
expectFailedEventScan(order, eventHandler,
- expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>());
+ expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ));
// Fire alarm to start next scan
dispatchBackgroundPeriodAlarm();
@@ -370,7 +369,7 @@
assertBackgroundPeriodAlarmPending();
expectFailedEventScan(order, eventHandler,
- expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ), new HashSet<Integer>());
+ expectedBandScanFreqs(WifiScanner.WIFI_BAND_24_GHZ));
verifyNoMoreInteractions(eventHandler);
}
@@ -401,7 +400,7 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// All scan starts succeed
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// Start scan
mScanner.startBatchedScan(settings, eventHandler);
@@ -455,7 +454,7 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// All scan starts succeed
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// Start scan
mScanner.startBatchedScan(settings, eventHandler);
@@ -528,7 +527,7 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// All scan starts succeed
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// Start scan
mScanner.startBatchedScan(settings, eventHandler);
@@ -566,7 +565,7 @@
InOrder order = inOrder(eventHandler, mWifiNative);
// All scans succeed
- when(mWifiNative.scan(any(Set.class), any(Set.class))).thenReturn(true);
+ when(mWifiNative.scan(any(), any(Set.class))).thenReturn(true);
// Start scan
mScanner.startBatchedScan(settings, eventHandler);
@@ -610,7 +609,7 @@
}
}
expectSuccessfulBackgroundScan(order, eventHandler, period.getScanFreqs(),
- new HashSet<Integer>(), nativeResults, scanDatas, fullResults, periodId);
+ nativeResults, scanDatas, fullResults, periodId);
}
/**
@@ -619,11 +618,11 @@
*/
private void expectSuccessfulBackgroundScan(InOrder order,
WifiNative.ScanEventHandler eventHandler, Set<Integer> scanFreqs,
- Set<Integer> networkIds, ArrayList<ScanDetail> nativeResults,
+ ArrayList<ScanDetail> nativeResults,
WifiScanner.ScanData[] expectedScanResults,
ScanResult[] fullResults, int periodId) {
// Verify scan started
- order.verify(mWifiNative).scan(eq(scanFreqs), eq(networkIds));
+ order.verify(mWifiNative).scan(eq(scanFreqs), any(Set.class));
// Setup scan results
when(mWifiNative.getScanResults()).thenReturn(nativeResults);
@@ -647,17 +646,15 @@
}
private void expectFailedScanStart(InOrder order, WifiNative.ScanEventHandler eventHandler,
- Set<Integer> scanFreqs, Set<Integer> networkIds) {
+ Set<Integer> scanFreqs) {
// Verify scan started
- order.verify(mWifiNative).scan(eq(scanFreqs), eq(networkIds));
-
- // TODO: verify failure event
+ order.verify(mWifiNative).scan(eq(scanFreqs), any(Set.class));
}
private void expectFailedEventScan(InOrder order, WifiNative.ScanEventHandler eventHandler,
- Set<Integer> scanFreqs, Set<Integer> networkIds) {
+ Set<Integer> scanFreqs) {
// Verify scan started
- order.verify(mWifiNative).scan(eq(scanFreqs), eq(networkIds));
+ order.verify(mWifiNative).scan(eq(scanFreqs), any(Set.class));
// Notify scan has failed
mWifiMonitor.sendMessage(mWifiNative.getInterfaceName(), WifiMonitor.SCAN_FAILED_EVENT);
@@ -668,17 +665,17 @@
private void assertBackgroundPeriodAlarmPending() {
assertTrue("background period alarm not pending",
- mAlarmManager.isPending(SupplicantWifiScannerImpl.BACKGROUND_PERIOD_ALARM_TAG));
+ mAlarmManager.isPending(WificondScannerImpl.BACKGROUND_PERIOD_ALARM_TAG));
}
private void assertBackgroundPeriodAlarmNotPending() {
assertFalse("background period alarm is pending",
- mAlarmManager.isPending(SupplicantWifiScannerImpl.BACKGROUND_PERIOD_ALARM_TAG));
+ mAlarmManager.isPending(WificondScannerImpl.BACKGROUND_PERIOD_ALARM_TAG));
}
private void dispatchBackgroundPeriodAlarm() {
assertTrue("dispatch background period alarm",
- mAlarmManager.dispatch(SupplicantWifiScannerImpl.BACKGROUND_PERIOD_ALARM_TAG));
+ mAlarmManager.dispatch(WificondScannerImpl.BACKGROUND_PERIOD_ALARM_TAG));
mLooper.dispatchAll();
}
@@ -691,7 +688,7 @@
public final boolean result;
public final boolean full;
- private ReportType(boolean result, boolean full) {
+ ReportType(boolean result, boolean full) {
this.result = result;
this.full = full;
}
@@ -700,12 +697,12 @@
private final ScanResults[] mDeliveredResults;
private final Set<Integer> mRequestedFreqs;
- public ScanPeriod(ReportType reportType, ScanResults deliveredResult,
+ ScanPeriod(ReportType reportType, ScanResults deliveredResult,
Set<Integer> requestedFreqs) {
this(reportType, new ScanResults[] {deliveredResult}, requestedFreqs);
}
- public ScanPeriod(ReportType reportType, ScanResults[] deliveredResults,
+ ScanPeriod(ReportType reportType, ScanResults[] deliveredResults,
Set<Integer> requestedFreqs) {
mReportType = reportType;
mDeliveredResults = deliveredResults;
diff --git a/tests/wifitests/src/com/android/server/wifi/util/BitMaskTest.java b/tests/wifitests/src/com/android/server/wifi/util/BitMaskTest.java
new file mode 100644
index 0000000..af7ad04
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/util/BitMaskTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.util.BitMask}.
+ */
+public class BitMaskTest {
+ /**
+ * Test that checkoff.testAndClear works as advertised
+ */
+ @Test
+ public void testBitMask() throws Exception {
+
+ BitMask checkoff = new BitMask(0x53);
+
+ Assert.assertTrue(checkoff.testAndClear(0x10)); // First time, bit should be there
+ Assert.assertFalse(checkoff.testAndClear(0x10)); // Second time, should be gone
+ Assert.assertFalse(checkoff.testAndClear(0x100)); // This bit was not set
+ Assert.assertEquals(0x43, checkoff.value); // These should be left
+ }
+}
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/InformationElementUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
index ef9e120..b9a8c31 100644
--- a/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/util/InformationElementUtilTest.java
@@ -18,10 +18,14 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import android.net.wifi.ScanResult.InformationElement;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.wifi.hotspot2.NetworkDetail;
+
import org.junit.Test;
import java.io.ByteArrayOutputStream;
@@ -239,7 +243,7 @@
}
/**
- * Test Capabilities.buildCapabilities() with a RSN IE.
+ * Test Capabilities.generateCapabilitiesString() with a RSN IE.
* Expect the function to return a string with the proper security information.
*/
@Test
@@ -258,13 +262,42 @@
BitSet beaconCap = new BitSet(16);
beaconCap.set(4);
- String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap);
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
- assertEquals("[WPA2-PSK]", result);
+ assertEquals("[WPA2-PSK-CCMP+TKIP]", result);
}
/**
- * Test Capabilities.buildCapabilities() with a WPA type 1 IE.
+ * Test Capabilities.generateCapabilitiesString() with a RSN IE which is malformed.
+ * Expect the function to return a string with empty key management & pairswise cipher security
+ * information.
+ */
+ @Test
+ public void buildCapabilities_malformedRsnElement() {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_RSN;
+ ie.bytes = new byte[] { (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x0F,
+ (byte) 0xAC, (byte) 0x02, (byte) 0x02, (byte) 0x00,
+ (byte) 0x00, (byte) 0x0F, (byte) 0xAC };
+
+ InformationElement[] ies = new InformationElement[] { ie };
+
+ BitSet beaconCap = new BitSet(16);
+ beaconCap.set(4);
+
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
+
+ assertEquals("[WPA2]", result);
+ }
+
+ /**
+ * Test Capabilities.generateCapabilitiesString() with a WPA type 1 IE.
* Expect the function to return a string with the proper security information.
*/
@Test
@@ -283,14 +316,144 @@
BitSet beaconCap = new BitSet(16);
beaconCap.set(4);
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
- String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap);
-
- assertEquals("[WPA-PSK]", result);
+ assertEquals("[WPA-PSK-CCMP+TKIP]", result);
}
/**
- * Test Capabilities.buildCapabilities() with a vendor specific element which
+ * Test Capabilities.generateCapabilitiesString() with a WPA type 1 IE which is malformed.
+ * Expect the function to return a string with empty key management & pairswise cipher security
+ * information.
+ */
+ @Test
+ public void buildCapabilities_malformedWpa1Element() {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_VSA;
+ ie.bytes = new byte[] { (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x01,
+ (byte) 0x01, (byte) 0x00 };
+
+ InformationElement[] ies = new InformationElement[] { ie };
+
+ BitSet beaconCap = new BitSet(16);
+ beaconCap.set(4);
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
+
+ assertEquals("[WPA]", result);
+ }
+
+ /**
+ * Test Capabilities.generateCapabilitiesString() with both RSN and WPA1 IE.
+ * Expect the function to return a string with the proper security information.
+ */
+ @Test
+ public void buildCapabilities_rsnAndWpaElement() {
+ InformationElement ieRsn = new InformationElement();
+ ieRsn.id = InformationElement.EID_RSN;
+ ieRsn.bytes = new byte[] { (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x0F,
+ (byte) 0xAC, (byte) 0x02, (byte) 0x02, (byte) 0x00,
+ (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04,
+ (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x02,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x0F,
+ (byte) 0xAC, (byte) 0x02, (byte) 0x00, (byte) 0x00 };
+
+ InformationElement ieWpa = new InformationElement();
+ ieWpa.id = InformationElement.EID_VSA;
+ ieWpa.bytes = new byte[] { (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x01,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x50,
+ (byte) 0xF2, (byte) 0x02, (byte) 0x02, (byte) 0x00,
+ (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x04,
+ (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x02,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x50,
+ (byte) 0xF2, (byte) 0x02, (byte) 0x00, (byte) 0x00 };
+
+ InformationElement[] ies = new InformationElement[] { ieWpa, ieRsn };
+
+ BitSet beaconCap = new BitSet(16);
+ beaconCap.set(4);
+
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
+
+ assertEquals("[WPA-PSK-CCMP+TKIP][WPA2-PSK-CCMP+TKIP]", result);
+ }
+
+ /**
+ * Test Capabilities.generateCapabilitiesString() with both RSN and WPA1 IE which are malformed.
+ * Expect the function to return a string with empty key management & pairswise cipher security
+ * information.
+ */
+ @Test
+ public void buildCapabilities_malformedRsnAndWpaElement() {
+ InformationElement ieRsn = new InformationElement();
+ ieRsn.id = InformationElement.EID_RSN;
+ ieRsn.bytes = new byte[] { (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x0F,
+ (byte) 0xAC, (byte) 0x02, (byte) 0x02 };
+
+ InformationElement ieWpa = new InformationElement();
+ ieWpa.id = InformationElement.EID_VSA;
+ ieWpa.bytes = new byte[] { (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x01,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x50,
+ (byte) 0xF2, (byte) 0x02, (byte) 0x02, (byte) 0x00,
+ (byte) 0x00, (byte) 0x50 };
+
+ InformationElement[] ies = new InformationElement[] { ieWpa, ieRsn };
+
+ BitSet beaconCap = new BitSet(16);
+ beaconCap.set(4);
+
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
+
+ assertEquals("[WPA][WPA2]", result);
+ }
+
+ /**
+ * Test Capabilities.generateCapabilitiesString() with both WPS and WPA1 IE.
+ * Expect the function to return a string with the proper security information.
+ */
+ @Test
+ public void buildCapabilities_wpaAndWpsElement() {
+ InformationElement ieWpa = new InformationElement();
+ ieWpa.id = InformationElement.EID_VSA;
+ ieWpa.bytes = new byte[] { (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x01,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x50,
+ (byte) 0xF2, (byte) 0x02, (byte) 0x02, (byte) 0x00,
+ (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x04,
+ (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x02,
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x50,
+ (byte) 0xF2, (byte) 0x02, (byte) 0x00, (byte) 0x00 };
+
+ InformationElement ieWps = new InformationElement();
+ ieWps.id = InformationElement.EID_VSA;
+ ieWps.bytes = new byte[] { (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x04 };
+
+ InformationElement[] ies = new InformationElement[] { ieWpa, ieWps };
+
+ BitSet beaconCap = new BitSet(16);
+ beaconCap.set(4);
+
+
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
+
+ assertEquals("[WPA-PSK-CCMP+TKIP][WPS]", result);
+ }
+
+ /**
+ * Test Capabilities.generateCapabilitiesString() with a vendor specific element which
* is not WPA type 1. Beacon Capability Information field has the Privacy
* bit set.
*
@@ -309,13 +472,17 @@
BitSet beaconCap = new BitSet(16);
beaconCap.set(4);
- String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap);
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
+
assertEquals("[WEP]", result);
}
/**
- * Test Capabilities.buildCapabilities() with a vendor specific element which
+ * Test Capabilities.generateCapabilitiesString() with a vendor specific element which
* is not WPA type 1. Beacon Capability Information field doesn't have the
* Privacy bit set.
*
@@ -334,13 +501,17 @@
BitSet beaconCap = new BitSet(16);
beaconCap.clear(4);
- String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap);
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
+
assertEquals("", result);
}
/**
- * Test Capabilities.buildCapabilities() with a vendor specific element which
+ * Test Capabilities.generateCapabilitiesString() with a vendor specific element which
* is not WPA type 1. Beacon Capability Information field has the ESS bit set.
*
* Expect the function to return a string with [ESS] there.
@@ -358,13 +529,17 @@
BitSet beaconCap = new BitSet(16);
beaconCap.set(0);
- String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap);
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
+
assertEquals("[ESS]", result);
}
/**
- * Test Capabilities.buildCapabilities() with a vendor specific element which
+ * Test Capabilities.generateCapabilitiesString() with a vendor specific element which
* is not WPA type 1. Beacon Capability Information field doesn't have the
* ESS bit set.
*
@@ -383,12 +558,71 @@
BitSet beaconCap = new BitSet(16);
beaconCap.clear(0);
- String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap);
+ InformationElementUtil.Capabilities capabilities =
+ new InformationElementUtil.Capabilities();
+ capabilities.from(ies, beaconCap);
+ String result = capabilities.generateCapabilitiesString();
+
assertEquals("", result);
}
/**
+ * Verify the expectations when building an ExtendedCapabilites IE from data with no bits set.
+ * Both ExtendedCapabilities#isStrictUtf8() and ExtendedCapabilites#is80211McRTTResponder()
+ * should return false.
+ */
+ @Test
+ public void buildExtendedCapabilities_emptyBitSet() {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_EXTENDED_CAPS;
+ ie.bytes = new byte[8];
+
+ InformationElementUtil.ExtendedCapabilities extendedCap =
+ new InformationElementUtil.ExtendedCapabilities();
+ extendedCap.from(ie);
+ assertFalse(extendedCap.isStrictUtf8());
+ assertFalse(extendedCap.is80211McRTTResponder());
+ }
+
+ /**
+ * Verify the expectations when building an ExtendedCapabilites IE from data with UTF-8 SSID
+ * bit set (bit 48). ExtendedCapabilities#isStrictUtf8() should return true.
+ */
+ @Test
+ public void buildExtendedCapabilites_strictUtf8() {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_EXTENDED_CAPS;
+ ie.bytes = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00 };
+
+ InformationElementUtil.ExtendedCapabilities extendedCap =
+ new InformationElementUtil.ExtendedCapabilities();
+ extendedCap.from(ie);
+ assertTrue(extendedCap.isStrictUtf8());
+ assertFalse(extendedCap.is80211McRTTResponder());
+ }
+
+ /**
+ * Verify the expectations when building an ExtendedCapabilites IE from data with RTT Response
+ * Enable bit set (bit 70). ExtendedCapabilities#is80211McRTTResponder() should return true.
+ */
+ @Test
+ public void buildExtendedCapabilites_80211McRTTResponder() {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_EXTENDED_CAPS;
+ ie.bytes = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x40 };
+
+ InformationElementUtil.ExtendedCapabilities extendedCap =
+ new InformationElementUtil.ExtendedCapabilities();
+ extendedCap.from(ie);
+ assertFalse(extendedCap.isStrictUtf8());
+ assertTrue(extendedCap.is80211McRTTResponder());
+ }
+
+ /**
* Test a that a correctly formed TIM Information Element is decoded into a valid TIM element,
* and the values are captured
*/
@@ -437,4 +671,86 @@
trafficIndicationMap.from(ie);
assertEquals(trafficIndicationMap.isValid(), false);
}
+
+ /**
+ * Verify that the expected Roaming Consortium information element is parsed and retrieved
+ * from the list of IEs.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getRoamingConsortiumIE() throws Exception {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_ROAMING_CONSORTIUM;
+ /**
+ * Roaming Consortium Format;
+ * | Number of OIs | OI#1 and OI#2 Lengths | OI #1 | OI #2 (optional) | OI #3 (optional) |
+ * 1 1 variable variable variable
+ */
+ ie.bytes = new byte[] { (byte) 0x01 /* number of OIs */, (byte) 0x03 /* OI Length */,
+ (byte) 0x11, (byte) 0x22, (byte) 0x33};
+ InformationElementUtil.RoamingConsortium roamingConsortium =
+ InformationElementUtil.getRoamingConsortiumIE(new InformationElement[] {ie});
+ assertEquals(1, roamingConsortium.anqpOICount);
+ assertEquals(1, roamingConsortium.roamingConsortiums.length);
+ assertEquals(0x112233, roamingConsortium.roamingConsortiums[0]);
+ }
+
+ /**
+ * Verify that the expected Hotspot 2.0 Vendor Specific information element is parsed and
+ * retrieved from the list of IEs.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getHS2VendorSpecificIE() throws Exception {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_VSA;
+ /**
+ * Vendor Specific OI Format:
+ * | OI | Type | Hotspot Configuration | PPS MO ID (optional) | ANQP Domain ID (optional)
+ * 3 1 1 2 2
+ *
+ * With OI=0x506F9A and Type=0x10 for Hotspot 2.0
+ *
+ * The Format of Hotspot Configuration:
+ * B0 B1 B2 B3 B4 B7
+ * | DGAF Disabled | PPS MO ID Flag | ANQP Domain ID Flag | reserved | Release Number |
+ */
+ ie.bytes = new byte[] { (byte) 0x50, (byte) 0x6F, (byte) 0x9A, (byte) 0x10,
+ (byte) 0x14 /* Hotspot Configuration */, (byte) 0x11, (byte) 0x22};
+ InformationElementUtil.Vsa vsa =
+ InformationElementUtil.getHS2VendorSpecificIE(new InformationElement[] {ie});
+ assertEquals(NetworkDetail.HSRelease.R2, vsa.hsRelease);
+ assertEquals(0x2211, vsa.anqpDomainID);
+ }
+
+ /**
+ * Verify that the expected Interworking information element is parsed and retrieved from the
+ * list of IEs.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void getInterworkingElementIE() throws Exception {
+ InformationElement ie = new InformationElement();
+ ie.id = InformationElement.EID_INTERWORKING;
+ /**
+ * Interworking Format:
+ * | Access Network Option | Venue Info (optional) | HESSID (optional) |
+ * 1 2 6
+ *
+ * Access Network Option Format:
+ *
+ * B0 B3 B4 B5 B6 B7
+ * | Access Network Type | Internet | ASRA | ESR | UESA |
+ */
+ ie.bytes = new byte[] { (byte) 0x10, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44,
+ (byte) 0x55, (byte) 0x66 };
+ InformationElementUtil.Interworking interworking =
+ InformationElementUtil.getInterworkingIE(new InformationElement[] {ie});
+ assertTrue(interworking.internet);
+ assertEquals(NetworkDetail.Ant.Private, interworking.ant);
+ assertEquals(0x112233445566L, interworking.hessid);
+ }
}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java
new file mode 100644
index 0000000..3f51c5a
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/util/NativeUtilTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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.*;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.util.NativeUtil}.
+ */
+public class NativeUtilTest {
+ /**
+ * Test that parsing a typical colon-delimited MAC address works.
+ */
+ @Test
+ public void testMacAddressToByteArray() throws Exception {
+ assertArrayEquals(new byte[]{0x61, 0x52, 0x43, 0x34, 0x25, 0x16},
+ NativeUtil.macAddressToByteArray("61:52:43:34:25:16"));
+ }
+
+ /**
+ * Test that parsing an empty MAC address works.
+ */
+ @Test
+ public void testEmptyMacAddressToByteArray() throws Exception {
+ assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0},
+ NativeUtil.macAddressToByteArray(""));
+ assertArrayEquals(new byte[]{0, 0, 0, 0, 0, 0},
+ NativeUtil.macAddressToByteArray(null));
+ }
+
+ /**
+ * Test that conversion of byte array of mac address to typical colon-delimited MAC address
+ * works.
+ */
+ @Test
+ public void testByteArrayToMacAddress() throws Exception {
+ assertEquals("61:52:43:34:25:16",
+ NativeUtil.macAddressFromByteArray(new byte[]{0x61, 0x52, 0x43, 0x34, 0x25, 0x16}));
+ }
+
+ /**
+ * Test that parsing a typical colon-delimited MAC OUI address works.
+ */
+ @Test
+ public void testMacAddressOuiToByteArray() throws Exception {
+ assertArrayEquals(new byte[]{0x61, 0x52, 0x43},
+ NativeUtil.macAddressOuiToByteArray("61:52:43"));
+ }
+
+ /**
+ * Test that parsing a hex string to byte array works.
+ */
+ @Test
+ public void testHexStringToByteArray() throws Exception {
+ assertArrayEquals(new byte[]{0x45, 0x12, 0x23, 0x34},
+ NativeUtil.hexStringToByteArray("45122334"));
+ }
+
+ /**
+ * Test that conversion of byte array to hexstring works.
+ */
+ @Test
+ public void testHexStringFromByteArray() throws Exception {
+ assertEquals("45122334",
+ NativeUtil.hexStringFromByteArray(new byte[]{0x45, 0x12, 0x23, 0x34}));
+ }
+
+ /**
+ * Test that conversion of ssid bytes to quoted ASCII string ssid works.
+ */
+ @Test
+ public void testAsciiSsidDecode() throws Exception {
+ assertEquals(
+ new ArrayList<>(
+ Arrays.asList((byte) 's', (byte) 's', (byte) 'i', (byte) 'd', (byte) '_',
+ (byte) 't', (byte) 'e', (byte) 's', (byte) 't', (byte) '1',
+ (byte) '2', (byte) '3')),
+ NativeUtil.decodeSsid("\"ssid_test123\""));
+ }
+
+ /**
+ * Test that conversion of ssid bytes to hex string ssid works.
+ */
+ @Test
+ public void testNonAsciiSsidDecode() throws Exception {
+ assertEquals(
+ new ArrayList<>(
+ Arrays.asList((byte) 0xf5, (byte) 0xe4, (byte) 0xab, (byte) 0x78,
+ (byte) 0xab, (byte) 0x34, (byte) 0x32, (byte) 0x43, (byte) 0x9a)),
+ NativeUtil.decodeSsid("f5e4ab78ab3432439a"));
+ }
+
+ /**
+ * Test that conversion of ssid bytes to quoted ASCII string ssid works.
+ */
+ @Test
+ public void testAsciiSsidEncode() throws Exception {
+ assertEquals(
+ "\"ssid_test123\"",
+ NativeUtil.encodeSsid(new ArrayList<>(
+ Arrays.asList((byte) 's', (byte) 's', (byte) 'i', (byte) 'd', (byte) '_',
+ (byte) 't', (byte) 'e', (byte) 's', (byte) 't', (byte) '1',
+ (byte) '2', (byte) '3'))));
+ }
+
+ /**
+ * Test that conversion of byte array to hex string works when the byte array contains 0.
+ */
+ @Test
+ public void testNullCharInAsciiSsidEncode() throws Exception {
+ assertEquals(
+ "007369645f74657374313233",
+ NativeUtil.encodeSsid(new ArrayList<>(
+ Arrays.asList((byte) 0x00, (byte) 's', (byte) 'i', (byte) 'd', (byte) '_',
+ (byte) 't', (byte) 'e', (byte) 's', (byte) 't', (byte) '1',
+ (byte) '2', (byte) '3'))));
+ }
+
+ /**
+ * Test that conversion of byte array to hex string ssid works.
+ */
+ @Test
+ public void testNonAsciiSsidEncode() throws Exception {
+ assertEquals(
+ "f5e4ab78ab3432439a",
+ NativeUtil.encodeSsid(new ArrayList<>(
+ Arrays.asList((byte) 0xf5, (byte) 0xe4, (byte) 0xab, (byte) 0x78,
+ (byte) 0xab, (byte) 0x34, (byte) 0x32, (byte) 0x43, (byte) 0x9a))));
+ }
+
+ /**
+ * Test that parsing of quoted SSID to byte array and vice versa works.
+ */
+ @Test
+ public void testSsidEncodeDecode() throws Exception {
+ String ssid = "\"ssid_test123\"";
+ assertEquals(ssid, NativeUtil.encodeSsid(NativeUtil.decodeSsid(ssid)));
+ }
+
+ /**
+ * Test that the enclosing quotes are removed properly.
+ */
+ @Test
+ public void testRemoveEnclosingQuotes() throws Exception {
+ assertEquals("abcdefgh", NativeUtil.removeEnclosingQuotes("\"abcdefgh\""));
+ assertEquals("abcdefgh", NativeUtil.removeEnclosingQuotes("abcdefgh"));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/ScanDetailUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/ScanDetailUtilTest.java
deleted file mode 100644
index 07d2521..0000000
--- a/tests/wifitests/src/com/android/server/wifi/util/ScanDetailUtilTest.java
+++ /dev/null
@@ -1,122 +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 static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import android.net.wifi.ScanResult;
-import android.net.wifi.ScanResult.InformationElement;
-import android.net.wifi.WifiSsid;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.server.wifi.ScanDetail;
-
-import org.junit.Test;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-
-/**
- * Unit tests for {@link com.android.server.wifi.util.ScanDetailUtil}.
- */
-@SmallTest
-public class ScanDetailUtilTest {
-
- @Test
- public void convertScanResult() {
- final String ssid = "SOME SsId";
-
- ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid,
- "ab:cd:01:ef:45:89", 1245, 0, "", -78, 2450, 1025, 22, 33, 20, 0, 0, true);
-
- input.informationElements = new InformationElement[] {
- createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
- };
-
- ScanDetail output = ScanDetailUtil.toScanDetail(input);
-
- validateScanDetail(input, output);
- }
-
- @Test
- public void convertScanResultWithAnqpLines() {
- final String ssid = "SOME SsId";
-
- ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid,
- "ab:cd:01:ef:45:89", 1245, 0, "some caps", -78, 2450, 1025, 22, 33, 20, 0, 0, true);
-
- input.informationElements = new InformationElement[] {
- createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
- };
- input.anqpLines = Arrays.asList("LINE 1", "line 2", "Line 3");
-
- ScanDetail output = ScanDetailUtil.toScanDetail(input);
-
- validateScanDetail(input, output);
- }
-
- @Test
- public void convertScanResultWithoutWifiSsid() {
- final String ssid = "Another SSid";
- ScanResult input = new ScanResult(ssid, "ab:cd:01:ef:45:89", 1245, 0, "other caps",
- -78, 2450, 1025, 22, 33, 20, 0, 0, true);
- input.informationElements = new InformationElement[] {
- createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
- };
-
- ScanDetail output = ScanDetailUtil.toScanDetail(input);
-
- validateScanDetail(input, output);
- }
-
- private static InformationElement createIE(int id, byte[] bytes) {
- InformationElement ie = new InformationElement();
- ie.id = id;
- ie.bytes = bytes;
- return ie;
- }
-
- private static void validateScanDetail(ScanResult input, ScanDetail output) {
- assertNotNull("NetworkDetail was null", output.getNetworkDetail());
- assertNotNull("ScanResult was null", output.getScanResult());
- assertEquals("NetworkDetail SSID", input.SSID,
- output.getNetworkDetail().getSSID());
- assertEquals("ScanResult SSID", input.SSID,
- output.getScanResult().SSID);
- assertEquals("ScanResult wifiSsid", input.wifiSsid,
- output.getScanResult().wifiSsid);
- assertEquals("getSSID", input.SSID, output.getSSID());
- assertEquals("NetworkDetail BSSID", input.BSSID,
- output.getNetworkDetail().getBSSIDString());
- assertEquals("getBSSIDString", input.BSSID, output.getBSSIDString());
- assertEquals("ScanResult frequency", input.frequency,
- output.getScanResult().frequency);
- assertEquals("ScanResult level", input.level,
- output.getScanResult().level);
- assertEquals("ScanResult capabilities", input.capabilities,
- output.getScanResult().capabilities);
- assertEquals("ScanResult timestamp", input.timestamp,
- output.getScanResult().timestamp);
- assertArrayEquals("ScanResult information elements", input.informationElements,
- output.getScanResult().informationElements);
- assertEquals("ScanResult anqp lines", input.anqpLines,
- output.getScanResult().anqpLines);
- }
-
-}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
new file mode 100644
index 0000000..7b03698
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/util/ScanResultUtilTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.*;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.ScanResult.InformationElement;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiSsid;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.wifi.ScanDetail;
+
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.util.ScanResultUtil}.
+ */
+@SmallTest
+public class ScanResultUtilTest {
+
+ @Test
+ public void convertScanResult() {
+ final String ssid = "SOME SsId";
+
+ ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid,
+ "ab:cd:01:ef:45:89", 1245, 0, "", -78, 2450, 1025, 22, 33, 20, 0, 0, true);
+
+ input.informationElements = new InformationElement[] {
+ createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
+ };
+
+ ScanDetail output = ScanResultUtil.toScanDetail(input);
+
+ validateScanDetail(input, output);
+ }
+
+ @Test
+ public void convertScanResultWithAnqpLines() {
+ final String ssid = "SOME SsId";
+
+ ScanResult input = new ScanResult(WifiSsid.createFromAsciiEncoded(ssid), ssid,
+ "ab:cd:01:ef:45:89", 1245, 0, "some caps", -78, 2450, 1025, 22, 33, 20, 0, 0, true);
+
+ input.informationElements = new InformationElement[] {
+ createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
+ };
+ input.anqpLines = Arrays.asList("LINE 1", "line 2", "Line 3");
+
+ ScanDetail output = ScanResultUtil.toScanDetail(input);
+
+ validateScanDetail(input, output);
+ }
+
+ @Test
+ public void convertScanResultWithoutWifiSsid() {
+ final String ssid = "Another SSid";
+ ScanResult input = new ScanResult(ssid, "ab:cd:01:ef:45:89", 1245, 0, "other caps",
+ -78, 2450, 1025, 22, 33, 20, 0, 0, true);
+ input.informationElements = new InformationElement[] {
+ createIE(InformationElement.EID_SSID, ssid.getBytes(StandardCharsets.UTF_8))
+ };
+
+ ScanDetail output = ScanResultUtil.toScanDetail(input);
+
+ validateScanDetail(input, output);
+ }
+
+ @Test
+ public void testScanResultMatchingWithNetwork() {
+ final String ssid = "Another SSid";
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = ScanResultUtil.createQuotedSSID(ssid);
+ ScanResult scanResult = new ScanResult(ssid, "ab:cd:01:ef:45:89", 1245, 0, "",
+ -78, 2450, 1025, 22, 33, 20, 0, 0, true);
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ scanResult.capabilities = "";
+ assertTrue(ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config));
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ config.wepKeys[0] = "45592364648547";
+ scanResult.capabilities = "WEP";
+ assertTrue(ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config));
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ scanResult.capabilities = "PSK";
+ assertTrue(ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config));
+
+ config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+ scanResult.capabilities = "EAP";
+ assertTrue(ScanResultUtil.doesScanResultMatchWithNetwork(scanResult, config));
+ }
+
+ @Test
+ public void testNetworkCreationFromScanResult() {
+ final String ssid = "Another SSid";
+ ScanResult scanResult = new ScanResult(ssid, "ab:cd:01:ef:45:89", 1245, 0, "",
+ -78, 2450, 1025, 22, 33, 20, 0, 0, true);
+ WifiConfiguration config;
+
+ scanResult.capabilities = "";
+ config = ScanResultUtil.createNetworkFromScanResult(scanResult);
+ assertEquals(config.SSID, ScanResultUtil.createQuotedSSID(ssid));
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE));
+
+ scanResult.capabilities = "WEP";
+ config = ScanResultUtil.createNetworkFromScanResult(scanResult);
+ assertEquals(config.SSID, ScanResultUtil.createQuotedSSID(ssid));
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE));
+ assertTrue(config.allowedAuthAlgorithms.get(WifiConfiguration.AuthAlgorithm.OPEN));
+ assertTrue(config.allowedAuthAlgorithms.get(WifiConfiguration.AuthAlgorithm.SHARED));
+
+ scanResult.capabilities = "PSK";
+ config = ScanResultUtil.createNetworkFromScanResult(scanResult);
+ assertEquals(config.SSID, ScanResultUtil.createQuotedSSID(ssid));
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK));
+
+ scanResult.capabilities = "EAP";
+ config = ScanResultUtil.createNetworkFromScanResult(scanResult);
+ assertEquals(config.SSID, ScanResultUtil.createQuotedSSID(ssid));
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP));
+ assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X));
+ }
+
+ private static InformationElement createIE(int id, byte[] bytes) {
+ InformationElement ie = new InformationElement();
+ ie.id = id;
+ ie.bytes = bytes;
+ return ie;
+ }
+
+ private static void validateScanDetail(ScanResult input, ScanDetail output) {
+ assertNotNull("NetworkDetail was null", output.getNetworkDetail());
+ assertNotNull("ScanResult was null", output.getScanResult());
+ assertEquals("NetworkDetail SSID", input.SSID,
+ output.getNetworkDetail().getSSID());
+ assertEquals("ScanResult SSID", input.SSID,
+ output.getScanResult().SSID);
+ assertEquals("ScanResult wifiSsid", input.wifiSsid,
+ output.getScanResult().wifiSsid);
+ assertEquals("getSSID", input.SSID, output.getSSID());
+ assertEquals("NetworkDetail BSSID", input.BSSID,
+ output.getNetworkDetail().getBSSIDString());
+ assertEquals("getBSSIDString", input.BSSID, output.getBSSIDString());
+ assertEquals("ScanResult frequency", input.frequency,
+ output.getScanResult().frequency);
+ assertEquals("ScanResult level", input.level,
+ output.getScanResult().level);
+ assertEquals("ScanResult capabilities", input.capabilities,
+ output.getScanResult().capabilities);
+ assertEquals("ScanResult timestamp", input.timestamp,
+ output.getScanResult().timestamp);
+ assertArrayEquals("ScanResult information elements", input.informationElements,
+ output.getScanResult().informationElements);
+ assertEquals("ScanResult anqp lines", input.anqpLines,
+ output.getScanResult().anqpLines);
+ }
+
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java
new file mode 100644
index 0000000..240096a
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/util/TelephonyUtilTest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.*;
+import static org.mockito.Mockito.*;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import com.android.server.wifi.WifiConfigurationTestUtil;
+import com.android.server.wifi.util.TelephonyUtil.SimAuthRequestData;
+import com.android.server.wifi.util.TelephonyUtil.SimAuthResponseData;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.util.TelephonyUtil}.
+ */
+@SmallTest
+public class TelephonyUtilTest {
+ @Test
+ public void getSimIdentityEapSim() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ when(tm.getSubscriberId()).thenReturn("3214561234567890");
+ when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+ when(tm.getSimOperator()).thenReturn("321456");
+ assertEquals("13214561234567890@wlan.mnc456.mcc321.3gppnetwork.org",
+ TelephonyUtil.getSimIdentity(tm, WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)));
+ assertEquals("13214561234567890@wlan.mnc456.mcc321.3gppnetwork.org",
+ TelephonyUtil.getSimIdentity(tm, WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.SIM)));
+ }
+
+ @Test
+ public void getSimIdentityEapAka() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ when(tm.getSubscriberId()).thenReturn("3214561234567890");
+ when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+ when(tm.getSimOperator()).thenReturn("321456");
+ assertEquals("03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org",
+ TelephonyUtil.getSimIdentity(tm, WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.AKA, WifiEnterpriseConfig.Phase2.NONE)));
+ assertEquals("03214561234567890@wlan.mnc456.mcc321.3gppnetwork.org",
+ TelephonyUtil.getSimIdentity(tm, WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.AKA)));
+ }
+
+ @Test
+ public void getSimIdentityEapAkaPrime() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ when(tm.getSubscriberId()).thenReturn("3214561234567890");
+ when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+ when(tm.getSimOperator()).thenReturn("321456");
+ assertEquals("63214561234567890@wlan.mnc456.mcc321.3gppnetwork.org",
+ TelephonyUtil.getSimIdentity(tm, WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.AKA_PRIME, WifiEnterpriseConfig.Phase2.NONE)));
+ assertEquals("63214561234567890@wlan.mnc456.mcc321.3gppnetwork.org",
+ TelephonyUtil.getSimIdentity(tm, WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.AKA_PRIME)));
+ }
+
+ @Test
+ public void getSimIdentity2DigitMnc() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ when(tm.getSubscriberId()).thenReturn("321560123456789");
+ when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+ when(tm.getSimOperator()).thenReturn("32156");
+ assertEquals("1321560123456789@wlan.mnc056.mcc321.3gppnetwork.org",
+ TelephonyUtil.getSimIdentity(tm, WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)));
+ }
+
+ @Test
+ public void getSimIdentityUnknownMccMnc() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ when(tm.getSubscriberId()).thenReturn("3214560123456789");
+ when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_UNKNOWN);
+ when(tm.getSimOperator()).thenReturn(null);
+ assertEquals("13214560123456789@wlan.mnc456.mcc321.3gppnetwork.org",
+ TelephonyUtil.getSimIdentity(tm, WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)));
+ }
+
+ @Test
+ public void getSimIdentityWithNoTelephonyManager() {
+ assertEquals(null, TelephonyUtil.getSimIdentity(null,
+ WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)));
+ }
+
+ @Test
+ public void getSimIdentityNonTelephonyConfig() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ when(tm.getSubscriberId()).thenReturn("321560123456789");
+ when(tm.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY);
+ when(tm.getSimOperator()).thenReturn("32156");
+ assertEquals(null, TelephonyUtil.getSimIdentity(tm,
+ WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.TTLS, WifiEnterpriseConfig.Phase2.SIM)));
+ assertEquals(null, TelephonyUtil.getSimIdentity(tm,
+ WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.MSCHAPV2)));
+ assertEquals(null, TelephonyUtil.getSimIdentity(tm,
+ WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)));
+ assertEquals(null, TelephonyUtil.getSimIdentity(tm, new WifiConfiguration()));
+ }
+
+ @Test
+ public void isSimConfig() {
+ assertFalse(TelephonyUtil.isSimConfig(null));
+ assertFalse(TelephonyUtil.isSimConfig(new WifiConfiguration()));
+ assertFalse(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createOpenNetwork()));
+ assertFalse(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createWepNetwork()));
+ assertFalse(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createPskNetwork()));
+ assertFalse(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.TTLS, WifiEnterpriseConfig.Phase2.SIM)));
+ assertFalse(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.TLS, WifiEnterpriseConfig.Phase2.NONE)));
+ assertFalse(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.MSCHAPV2)));
+ assertTrue(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)));
+ assertTrue(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.AKA, WifiEnterpriseConfig.Phase2.NONE)));
+ assertTrue(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.AKA_PRIME, WifiEnterpriseConfig.Phase2.NONE)));
+ assertTrue(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.SIM)));
+ assertTrue(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.AKA)));
+ assertTrue(TelephonyUtil.isSimConfig(WifiConfigurationTestUtil.createEapNetwork(
+ WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.AKA_PRIME)));
+ }
+
+ /**
+ * Produce a base64 encoded length byte + data.
+ */
+ private static String createSimChallengeRequest(byte[] challengeValue) {
+ byte[] challengeLengthAndValue = new byte[challengeValue.length + 1];
+ challengeLengthAndValue[0] = (byte) challengeValue.length;
+ for (int i = 0; i < challengeValue.length; ++i) {
+ challengeLengthAndValue[i + 1] = challengeValue[i];
+ }
+ return Base64.encodeToString(challengeLengthAndValue, android.util.Base64.NO_WRAP);
+ }
+
+ /**
+ * Produce a base64 encoded sres length byte + sres + kc length byte + kc.
+ */
+ private static String createGsmSimAuthResponse(byte[] sresValue, byte[] kcValue) {
+ int overallLength = sresValue.length + kcValue.length + 2;
+ byte[] result = new byte[sresValue.length + kcValue.length + 2];
+ int idx = 0;
+ result[idx++] = (byte) sresValue.length;
+ for (int i = 0; i < sresValue.length; ++i) {
+ result[idx++] = sresValue[i];
+ }
+ result[idx++] = (byte) kcValue.length;
+ for (int i = 0; i < kcValue.length; ++i) {
+ result[idx++] = kcValue[i];
+ }
+ return Base64.encodeToString(result, Base64.NO_WRAP);
+ }
+
+ @Test
+ public void getGsmSimAuthResponseInvalidRequest() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ final String[] invalidRequests = { null, "", "XXXX" };
+ assertEquals("", TelephonyUtil.getGsmSimAuthResponse(invalidRequests, tm));
+ }
+
+ @Test
+ public void getGsmSimAuthResponseFailedSimResponse() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ final String[] failedRequests = { "5E5F" };
+ when(tm.getIccAuthentication(anyInt(), anyInt(),
+ eq(createSimChallengeRequest(new byte[] { 0x5e, 0x5f })))).thenReturn(null);
+
+ assertEquals(null, TelephonyUtil.getGsmSimAuthResponse(failedRequests, tm));
+ }
+
+ @Test
+ public void getGsmSimAuthResponseUsim() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM,
+ createSimChallengeRequest(new byte[] { 0x1b, 0x2b })))
+ .thenReturn(createGsmSimAuthResponse(new byte[] { 0x1D, 0x2C },
+ new byte[] { 0x3B, 0x4A }));
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM,
+ createSimChallengeRequest(new byte[] { 0x01, 0x22 })))
+ .thenReturn(createGsmSimAuthResponse(new byte[] { 0x11, 0x11 },
+ new byte[] { 0x12, 0x34 }));
+
+ assertEquals(":3b4a:1d2c:1234:1111", TelephonyUtil.getGsmSimAuthResponse(
+ new String[] { "1B2B", "0122" }, tm));
+ }
+
+ @Test
+ public void getGsmSimAuthResponseSimpleSim() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM,
+ createSimChallengeRequest(new byte[] { 0x1a, 0x2b })))
+ .thenReturn(null);
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_SIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM,
+ createSimChallengeRequest(new byte[] { 0x1a, 0x2b })))
+ .thenReturn(createGsmSimAuthResponse(new byte[] { 0x1D, 0x2C },
+ new byte[] { 0x3B, 0x4A }));
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM,
+ createSimChallengeRequest(new byte[] { 0x01, 0x23 })))
+ .thenReturn(null);
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_SIM,
+ TelephonyManager.AUTHTYPE_EAP_SIM,
+ createSimChallengeRequest(new byte[] { 0x01, 0x23 })))
+ .thenReturn(createGsmSimAuthResponse(new byte[] { 0x33, 0x22 },
+ new byte[] { 0x11, 0x00 }));
+
+ assertEquals(":3b4a:1d2c:1100:3322", TelephonyUtil.getGsmSimAuthResponse(
+ new String[] { "1A2B", "0123" }, tm));
+ }
+
+ /**
+ * Produce a base64 encoded tag + res length byte + res + ck length byte + ck + ik length byte +
+ * ik.
+ */
+ private static String create3GSimAuthUmtsAuthResponse(byte[] res, byte[] ck, byte[] ik) {
+ byte[] result = new byte[res.length + ck.length + ik.length + 4];
+ int idx = 0;
+ result[idx++] = (byte) 0xdb;
+ result[idx++] = (byte) res.length;
+ for (int i = 0; i < res.length; ++i) {
+ result[idx++] = res[i];
+ }
+ result[idx++] = (byte) ck.length;
+ for (int i = 0; i < ck.length; ++i) {
+ result[idx++] = ck[i];
+ }
+ result[idx++] = (byte) ik.length;
+ for (int i = 0; i < ik.length; ++i) {
+ result[idx++] = ik[i];
+ }
+ return Base64.encodeToString(result, Base64.NO_WRAP);
+ }
+
+ private static String create3GSimAuthUmtsAutsResponse(byte[] auts) {
+ byte[] result = new byte[auts.length + 2];
+ int idx = 0;
+ result[idx++] = (byte) 0xdc;
+ result[idx++] = (byte) auts.length;
+ for (int i = 0; i < auts.length; ++i) {
+ result[idx++] = auts[i];
+ }
+ return Base64.encodeToString(result, Base64.NO_WRAP);
+ }
+
+ @Test
+ public void get3GAuthResponseInvalidRequest() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+ assertEquals(null, TelephonyUtil.get3GAuthResponse(
+ new SimAuthRequestData(0, 0, "SSID", new String[] {"0123"}), tm));
+ assertEquals(null, TelephonyUtil.get3GAuthResponse(
+ new SimAuthRequestData(0, 0, "SSID", new String[] {"xyz2", "1234"}), tm));
+ verifyNoMoreInteractions(tm);
+ }
+
+ @Test
+ public void get3GAuthResponseNullIccAuthentication() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_AKA, "AgEjAkVn")).thenReturn(null);
+
+ SimAuthResponseData response = TelephonyUtil.get3GAuthResponse(
+ new SimAuthRequestData(0, 0, "SSID", new String[] {"0123", "4567"}), tm);
+ assertNull(response);
+ }
+
+ @Test
+ public void get3GAuthResponseIccAuthenticationTooShort() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_AKA, "AgEjAkVn"))
+ .thenReturn(Base64.encodeToString(new byte[] {(byte) 0xdc}, Base64.NO_WRAP));
+
+ SimAuthResponseData response = TelephonyUtil.get3GAuthResponse(
+ new SimAuthRequestData(0, 0, "SSID", new String[] {"0123", "4567"}), tm);
+ assertNull(response);
+ }
+
+ @Test
+ public void get3GAuthResponseBadTag() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_AKA, "AgEjAkVn"))
+ .thenReturn(Base64.encodeToString(new byte[] {0x31, 0x1, 0x2, 0x3, 0x4},
+ Base64.NO_WRAP));
+
+ SimAuthResponseData response = TelephonyUtil.get3GAuthResponse(
+ new SimAuthRequestData(0, 0, "SSID", new String[] {"0123", "4567"}), tm);
+ assertNull(response);
+ }
+
+ @Test
+ public void get3GAuthResponseUmtsAuth() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_AKA, "AgEjAkVn"))
+ .thenReturn(create3GSimAuthUmtsAuthResponse(new byte[] {0x11, 0x12},
+ new byte[] {0x21, 0x22, 0x23}, new byte[] {0x31}));
+
+ SimAuthResponseData response = TelephonyUtil.get3GAuthResponse(
+ new SimAuthRequestData(0, 0, "SSID", new String[] {"0123", "4567"}), tm);
+ assertNotNull(response);
+ assertEquals("UMTS-AUTH", response.type);
+ assertEquals(":31:212223:1112", response.response);
+ }
+
+ @Test
+ public void get3GAuthResponseUmtsAuts() {
+ TelephonyManager tm = mock(TelephonyManager.class);
+
+ when(tm.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
+ TelephonyManager.AUTHTYPE_EAP_AKA, "AgEjAkVn"))
+ .thenReturn(create3GSimAuthUmtsAutsResponse(new byte[] {0x22, 0x33}));
+
+ SimAuthResponseData response = TelephonyUtil.get3GAuthResponse(
+ new SimAuthRequestData(0, 0, "SSID", new String[] {"0123", "4567"}), tm);
+ assertNotNull(response);
+ assertEquals("UMTS-AUTS", response.type);
+ assertEquals(":2233", response.response);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/WifiHandlerTest.java b/tests/wifitests/src/com/android/server/wifi/util/WifiHandlerTest.java
new file mode 100644
index 0000000..220e6d7
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/util/WifiHandlerTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.os.Looper;
+import android.os.Message;
+import android.os.test.TestLooper;
+
+import com.android.server.wifi.FakeWifiLog;
+
+import static org.mockito.Matchers.contains;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+/** Unit tests for {@link WifiHandler}. */
+@RunWith(JUnit4.class)
+public class WifiHandlerTest {
+ private static final String TAG = "WifiHandlerTest";
+ private WifiHandler mCodeUnderTest;
+ @Spy FakeWifiLog mWifiLog;
+ TestLooper mLooper;
+
+ private class WifiHandlerTestClass extends WifiHandler {
+ WifiHandlerTestClass(String tag, Looper looper) {
+ super(tag, looper);
+ super.setWifiLog(mWifiLog);
+ }
+ }
+
+ @Before public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mLooper = new TestLooper();
+ mCodeUnderTest = new WifiHandlerTestClass(TAG, mLooper.getLooper());
+ }
+
+ @Test public void testHandleMessage() {
+ Message msg = Message.obtain();
+ msg.what = 0;
+ msg.sendingUid = 0;
+ mCodeUnderTest.handleMessage(msg);
+ verify(mWifiLog).trace(contains("message"));
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java b/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java
new file mode 100644
index 0000000..308e267
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/util/WifiPermissionsUtilTest.java
@@ -0,0 +1,538 @@
+/*
+ * 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.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.net.NetworkScoreManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.server.wifi.BinderUtil;
+import com.android.server.wifi.FakeWifiLog;
+import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.WifiSettingsStore;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+/** Unit tests for {@link WifiPermissionsUtil}. */
+@RunWith(JUnit4.class)
+public class WifiPermissionsUtilTest {
+ public static final String TAG = "WifiPermissionsUtilTest";
+
+ // Mock objects for testing
+ @Mock private WifiPermissionsWrapper mMockPermissionsWrapper;
+ @Mock private Context mMockContext;
+ @Mock private PackageManager mMockPkgMgr;
+ @Mock private ApplicationInfo mMockApplInfo;
+ @Mock private AppOpsManager mMockAppOps;
+ @Mock private UserInfo mMockUserInfo;
+ @Mock private UserManager mMockUserManager;
+ @Mock private WifiSettingsStore mMockWifiSettingsStore;
+ @Mock private ContentResolver mMockContentResolver;
+ @Mock private NetworkScoreManager mNetworkScoreManager;
+ @Mock private WifiInjector mWifiInjector;
+ @Spy private FakeWifiLog mWifiLog;
+
+ private static final String TEST_PACKAGE_NAME = "com.google.somePackage";
+ private static final String INVALID_PACKAGE = "BAD_PACKAGE";
+ private static final int MANAGED_PROFILE_UID = 1100000;
+ private static final int OTHER_USER_UID = 1200000;
+
+ private final int mCallingUser = UserHandle.USER_CURRENT_OR_SELF;
+ private final String mMacAddressPermission = "android.permission.PEERS_MAC_ADDRESS";
+ private final String mInteractAcrossUsersFullPermission =
+ "android.permission.INTERACT_ACROSS_USERS_FULL";
+ private final String mManifestStringCoarse =
+ Manifest.permission.ACCESS_COARSE_LOCATION;
+
+ // Test variables
+ private int mWifiScanAllowApps;
+ private int mUid;
+ private int mCoarseLocationPermission;
+ private int mAllowCoarseLocationApps;
+ private String mPkgNameOfTopActivity;
+ private int mCurrentUser;
+ private int mLocationModeSetting;
+ private boolean mThrowSecurityException;
+ private int mTargetVersion;
+ private boolean mActiveNwScorer;
+ private Answer<Integer> mReturnPermission;
+ private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>();
+
+ /**
+ * Set up Mockito tests
+ */
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ initTestVars();
+ }
+
+ private void setupTestCase() throws Exception {
+ setupMocks();
+ setupMockInterface();
+ }
+
+ /**
+ * Verify we return true when the UID does have the override config permission
+ */
+ @Test
+ public void testCheckConfigOverridePermissionApproved() throws Exception {
+ mUid = MANAGED_PROFILE_UID; // do not really care about this value
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ when(mMockPermissionsWrapper.getOverrideWifiConfigPermission(anyInt()))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ assertTrue(codeUnderTest.checkConfigOverridePermission(mUid));
+ }
+
+ /**
+ * Verify we return false when the UID does not have the override config permission.
+ */
+ @Test
+ public void testCheckConfigOverridePermissionDenied() throws Exception {
+ mUid = OTHER_USER_UID; // do not really care about this value
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ when(mMockPermissionsWrapper.getOverrideWifiConfigPermission(anyInt()))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ assertFalse(codeUnderTest.checkConfigOverridePermission(mUid));
+ }
+
+ /**
+ * Verify we return false when the override config permission check throws a RemoteException.
+ */
+ @Test
+ public void testCheckConfigOverridePermissionWithException() throws Exception {
+ mUid = OTHER_USER_UID; // do not really care about this value
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ doThrow(new RemoteException("Failed to check permissions for " + mUid))
+ .when(mMockPermissionsWrapper).getOverrideWifiConfigPermission(mUid);
+ assertFalse(codeUnderTest.checkConfigOverridePermission(mUid));
+ }
+
+ /**
+ * Test case setting: Package is valid
+ * Caller can read peers mac address
+ * This App has permission to request WIFI_SCAN
+ * User is current
+ * Validate result is true
+ * - User has all the permissions
+ */
+ @Test
+ public void testCanReadPeersMacAddressCurrentUserAndAllPermissions() throws Exception {
+ boolean output = false;
+ mThrowSecurityException = false;
+ mUid = MANAGED_PROFILE_UID;
+ mPermissionsList.put(mMacAddressPermission, mUid);
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ mCurrentUser = UserHandle.USER_CURRENT_OR_SELF;
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ assertEquals(output, true);
+ }
+
+ /**
+ * Test case setting: Package is valid
+ * Caller can read peers mac address
+ * This App has permission to request WIFI_SCAN
+ * User profile is current
+ * Validate result is true
+ * - User has all the permissions
+ */
+ @Test
+ public void testCanReadPeersMacAddressCurrentProfileAndAllPermissions() throws Exception {
+ boolean output = false;
+ mThrowSecurityException = false;
+ mUid = MANAGED_PROFILE_UID;
+ mPermissionsList.put(mMacAddressPermission, mUid);
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ mMockUserInfo.id = mCallingUser;
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ assertEquals(output, true);
+ }
+
+ /**
+ * Test case setting: Package is valid
+ * Caller can read peers mac address
+ * Validate result is false
+ * - This App doesn't have permission to request Wifi Scan
+ */
+ @Test
+ public void testCannotAccessScanResult_AppNotAllowed() throws Exception {
+ boolean output = true;
+ mThrowSecurityException = false;
+ mPermissionsList.put(mMacAddressPermission, mUid);
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ assertEquals(output, false);
+ }
+
+ /**
+ * Test case setting: Package is valid
+ * Caller can read peers mac address
+ * This App has permission to request WIFI_SCAN
+ * User or profile is not current but the uid has
+ * permission to INTERACT_ACROSS_USERS_FULL
+ * Validate result is true
+ * - User has all the permissions
+ */
+ @Test
+ public void testCanAccessScanResults_UserOrProfileNotCurrent() throws Exception {
+ boolean output = false;
+ mThrowSecurityException = false;
+ mUid = MANAGED_PROFILE_UID;
+ mPermissionsList.put(mMacAddressPermission, mUid);
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid);
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ assertEquals(output, true);
+ }
+
+ /**
+ * Test case setting: Package is valid
+ * Caller can read peers mac address
+ * This App has permission to request WIFI_SCAN
+ * User or profile is not Current
+ * Validate result is false
+ * - Calling uid doesn't have INTERACT_ACROSS_USERS_FULL permission
+ */
+ @Test
+ public void testCannotAccessScanResults_NoInteractAcrossUsersFullPermission() throws Exception {
+ boolean output = true;
+ mThrowSecurityException = false;
+ mUid = MANAGED_PROFILE_UID;
+ mPermissionsList.put(mMacAddressPermission, mUid);
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ assertEquals(output, false);
+ }
+
+ /**
+ * Test case setting: Package is valid
+ * Caller is active network scorer
+ * This App has permission to request WIFI_SCAN
+ * User is current
+ * Validate result is true
+ */
+ @Test
+ public void testCanAccessScanResults_CallerIsActiveNwScorer() throws Exception {
+ boolean output = false;
+ mThrowSecurityException = false;
+ mActiveNwScorer = true;
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ mCurrentUser = UserHandle.USER_CURRENT_OR_SELF;
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ assertEquals(output, true);
+ }
+
+ /**
+ * Test case Setting: Package is valid
+ * Legacy App
+ * Foreground
+ * This App has permission to request WIFI_SCAN
+ * User is current
+ * Validate result is true - has all permissions
+ */
+ @Test
+ public void testLegacyForegroundAppAndAllPermissions() throws Exception {
+ boolean output = false;
+ mThrowSecurityException = false;
+ mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.GINGERBREAD;
+ mPkgNameOfTopActivity = TEST_PACKAGE_NAME;
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ mUid = MANAGED_PROFILE_UID;
+ mCurrentUser = UserHandle.USER_CURRENT_OR_SELF;
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ assertEquals(output, true);
+ }
+
+ /**
+ * Test case Setting: Package is valid
+ * Legacy App
+ * Location Mode Enabled
+ * Coarse Location Access
+ * This App has permission to request WIFI_SCAN
+ * User profile is current
+ * Validate result is true - has all permissions
+ */
+ @Test
+ public void testLegacyAppHasLocationAndAllPermissions() throws Exception {
+ boolean output = false;
+ mThrowSecurityException = false;
+ mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.GINGERBREAD;
+ mLocationModeSetting = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
+ mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
+ mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ mUid = MANAGED_PROFILE_UID;
+ mMockUserInfo.id = mCallingUser;
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ assertEquals(output, true);
+ }
+
+ /**
+ * Test case setting: Package is valid
+ * Location Mode Enabled
+ * Validate result is false
+ * - Doesn't have Peer Mac Address read permission
+ * - Uid is not an active network scorer
+ * - Location Mode is enabled but the uid
+ * - doesn't have Coarse Location Access
+ * - which implies No Location Permission
+ */
+ @Test
+ public void testCannotAccessScanResults_NoCoarseLocationPermission() throws Exception {
+ boolean output = true;
+ mThrowSecurityException = false;
+ mLocationModeSetting = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ assertEquals(output, false);
+ }
+
+ /**
+ * Test case setting: Invalid Package
+ * Expect a securityException
+ */
+ @Test (expected = SecurityException.class)
+ public void testInvalidPackage() throws Exception {
+ boolean output = false;
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ try {
+ output = codeUnderTest.canAccessScanResults(TEST_PACKAGE_NAME, mUid, mTargetVersion);
+ } catch (SecurityException e) {
+ throw e;
+ }
+ }
+
+ /**
+ * Test case setting: caller does have Location permission.
+ * A SecurityException should not be thrown.
+ */
+ @Test
+ public void testEnforceLocationPermission() throws Exception {
+ mThrowSecurityException = false;
+ mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.GINGERBREAD;
+ mLocationModeSetting = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
+ mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
+ mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
+ mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
+ mUid = MANAGED_PROFILE_UID;
+ mMockUserInfo.id = mCallingUser;
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ codeUnderTest.enforceLocationPermission(TEST_PACKAGE_NAME, mUid);
+ }
+
+ /**
+ * Test case setting: caller does not have Location permission.
+ * Expect a SecurityException
+ */
+ @Test(expected = SecurityException.class)
+ public void testEnforceLocationPermissionExpectSecurityException() throws Exception {
+ setupTestCase();
+ WifiPermissionsUtil codeUnderTest = new WifiPermissionsUtil(mMockPermissionsWrapper,
+ mMockContext, mMockWifiSettingsStore, mMockUserManager, mNetworkScoreManager,
+ mWifiInjector);
+ codeUnderTest.enforceLocationPermission(TEST_PACKAGE_NAME, mUid);
+ }
+
+ private Answer<Integer> createPermissionAnswer() {
+ return new Answer<Integer>() {
+ @Override
+ public Integer answer(InvocationOnMock invocation) {
+ int myUid = (int) invocation.getArguments()[1];
+ String myPermission = (String) invocation.getArguments()[0];
+ mPermissionsList.get(myPermission);
+ if (mPermissionsList.containsKey(myPermission)) {
+ int uid = mPermissionsList.get(myPermission);
+ if (myUid == uid) {
+ return PackageManager.PERMISSION_GRANTED;
+ }
+ }
+ return PackageManager.PERMISSION_DENIED;
+ }
+ };
+ }
+
+ private void setupMocks() throws Exception {
+ when(mMockPkgMgr.getApplicationInfo(TEST_PACKAGE_NAME, 0))
+ .thenReturn(mMockApplInfo);
+ when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr);
+ when(mMockAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, mUid, TEST_PACKAGE_NAME))
+ .thenReturn(mWifiScanAllowApps);
+ when(mMockAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, mUid, TEST_PACKAGE_NAME))
+ .thenReturn(mAllowCoarseLocationApps);
+ if (mThrowSecurityException) {
+ doThrow(new SecurityException("Package " + TEST_PACKAGE_NAME + " doesn't belong"
+ + " to application bound to user " + mUid))
+ .when(mMockAppOps).checkPackage(mUid, TEST_PACKAGE_NAME);
+ }
+ when(mMockContext.getSystemService(Context.APP_OPS_SERVICE))
+ .thenReturn(mMockAppOps);
+ when(mMockUserManager.getProfiles(mCurrentUser))
+ .thenReturn(Arrays.asList(mMockUserInfo));
+ when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
+ when(mMockContext.getSystemService(Context.USER_SERVICE))
+ .thenReturn(mMockUserManager);
+ when(mWifiInjector.makeLog(anyString())).thenReturn(mWifiLog);
+ }
+
+ private void initTestVars() {
+ mPermissionsList.clear();
+ mReturnPermission = createPermissionAnswer();
+ mWifiScanAllowApps = AppOpsManager.MODE_ERRORED;
+ mUid = OTHER_USER_UID;
+ mThrowSecurityException = true;
+ mMockUserInfo.id = UserHandle.USER_NULL;
+ mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M;
+ mTargetVersion = Build.VERSION_CODES.M;
+ mPkgNameOfTopActivity = INVALID_PACKAGE;
+ mLocationModeSetting = Settings.Secure.LOCATION_MODE_OFF;
+ mCurrentUser = UserHandle.USER_SYSTEM;
+ mCoarseLocationPermission = PackageManager.PERMISSION_DENIED;
+ mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
+ mActiveNwScorer = false;
+ }
+
+ private void setupMockInterface() {
+ BinderUtil.setUid(mUid);
+ doAnswer(mReturnPermission).when(mMockPermissionsWrapper).getUidPermission(
+ anyString(), anyInt());
+ doAnswer(mReturnPermission).when(mMockPermissionsWrapper).getUidPermission(
+ anyString(), anyInt());
+ when(mMockPermissionsWrapper.getCallingUserId(mUid)).thenReturn(mCallingUser);
+ when(mMockPermissionsWrapper.getCurrentUser()).thenReturn(mCurrentUser);
+ when(mNetworkScoreManager.isCallerActiveScorer(mUid)).thenReturn(mActiveNwScorer);
+ when(mMockPermissionsWrapper.getUidPermission(mManifestStringCoarse, mUid))
+ .thenReturn(mCoarseLocationPermission);
+ when(mMockWifiSettingsStore.getLocationModeSetting(mMockContext))
+ .thenReturn(mLocationModeSetting);
+ when(mMockPermissionsWrapper.getTopPkgName()).thenReturn(mPkgNameOfTopActivity);
+ }
+}
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..0942b83
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/util/XmlUtilTest.java
@@ -0,0 +1,589 @@
+/*
+ * 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.*;
+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 {
+ public static final String XML_STRING_EAP_METHOD_REPLACE_FORMAT =
+ "<int name=\"EapMethod\" value=\"%d\" />";
+
+ 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.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
+ configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
+ configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+ configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
+ configuration.status = WifiConfiguration.Status.DISABLED;
+ configuration.linkedConfigurations = new HashMap<>();
+ configuration.linkedConfigurations.put(TEST_DUMMY_CONFIG_KEY, Integer.valueOf(1));
+ configuration.defaultGwMacAddress = TEST_STATIC_IP_GATEWAY_ADDRESS;
+ configuration.requirePMF = true;
+ 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 a WifiConfiguration with status as CURRENT when serializing
+ * is deserialized as ENABLED.
+ */
+ @Test
+ public void testCurrentStatusConfigurationSerializeDeserializeForConfigStore()
+ throws IOException, XmlPullParserException {
+ WifiConfiguration configuration = WifiConfigurationTestUtil.createEapNetwork();
+ configuration.status = WifiConfiguration.Status.CURRENT;
+ byte[] xmlData = serializeWifiConfigurationForConfigStore(configuration);
+ Pair<String, WifiConfiguration> deserializedConfiguration =
+ deserializeWifiConfiguration(xmlData);
+ assertEquals(WifiConfiguration.Status.ENABLED, deserializedConfiguration.second.status);
+ }
+
+ /**
+ * 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 network selection status deprecation is handled correctly during restore
+ * of data after upgrade.
+ * This test tries to simulate the scenario where we have a
+ * {@link NetworkSelectionStatus#getNetworkStatusString()} string stored
+ * in the XML file from a previous release which has now been deprecated. The network should
+ * be restored as enabled.
+ */
+ @Test
+ public void testDeprecatedNetworkSelectionStatusDeserialize()
+ throws IOException, XmlPullParserException {
+ // Create a dummy network selection status.
+ NetworkSelectionStatus status = new NetworkSelectionStatus();
+ status.setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
+ status.setNetworkSelectionDisableReason(
+ NetworkSelectionStatus.DISABLED_DHCP_FAILURE);
+ status.setConnectChoice(TEST_DUMMY_CONFIG_KEY);
+ status.setConnectChoiceTimestamp(867889);
+ status.setHasEverConnected(true);
+
+ // Serialize this to XML string.
+ byte[] xmlData = serializeNetworkSelectionStatus(status);
+
+ // Now modify the status string with some invalid string in XML data..
+ String xmlString = new String(xmlData);
+ String deprecatedXmlString =
+ xmlString.replaceAll(
+ status.getNetworkStatusString(), "NETWORK_SELECTION_DEPRECATED");
+ // Ensure that the modification did take effect.
+ assertFalse(xmlString.equals(deprecatedXmlString));
+
+ // Now Deserialize the modified XML data.
+ byte[] deprecatedXmlData = xmlString.getBytes();
+ NetworkSelectionStatus retrievedStatus =
+ deserializeNetworkSelectionStatus(deprecatedXmlData);
+
+ // The status retrieved should have reset both the |Status| & |DisableReason| fields after
+ // deserialization, but should have restored all the other fields correctly.
+ NetworkSelectionStatus expectedStatus = new NetworkSelectionStatus();
+ expectedStatus.copy(status);
+ expectedStatus.setNetworkSelectionStatus(NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
+ expectedStatus.setNetworkSelectionDisableReason(
+ NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+
+ WifiConfigurationTestUtil.assertNetworkSelectionStatusEqualForConfigStore(
+ expectedStatus, retrievedStatus);
+ }
+
+ /**
+ * Verify that a network selection disable reason deprecation is handled correctly during
+ * restore of data after upgrade.
+ * This test tries to simulate the scenario where we have a
+ * {@link NetworkSelectionStatus#getNetworkDisableReasonString()} ()} string stored
+ * in the XML file from a previous release which has now been deprecated. The network should
+ * be restored as enabled.
+ */
+ @Test
+ public void testDeprecatedNetworkSelectionDisableReasonDeserialize()
+ throws IOException, XmlPullParserException {
+ // Create a dummy network selection status.
+ NetworkSelectionStatus status = new NetworkSelectionStatus();
+ status.setNetworkSelectionStatus(
+ NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED);
+ status.setNetworkSelectionDisableReason(
+ NetworkSelectionStatus.DISABLED_DHCP_FAILURE);
+ status.setConnectChoice(TEST_DUMMY_CONFIG_KEY);
+ status.setConnectChoiceTimestamp(867889);
+ status.setHasEverConnected(true);
+
+ // Serialize this to XML string.
+ byte[] xmlData = serializeNetworkSelectionStatus(status);
+
+ // Now modify the disable reason string with some invalid string in XML data.
+ String xmlString = new String(xmlData);
+ String deprecatedXmlString =
+ xmlString.replaceAll(status.getNetworkDisableReasonString(), "DISABLED_DEPRECATED");
+ // Ensure that the modification did take effect.
+ assertFalse(xmlString.equals(deprecatedXmlString));
+
+ // Now Deserialize the modified XML data.
+ byte[] deprecatedXmlData = xmlString.getBytes();
+ NetworkSelectionStatus retrievedStatus =
+ deserializeNetworkSelectionStatus(deprecatedXmlData);
+
+ // The status retrieved should have reset both the |Status| & |DisableReason| fields after
+ // deserialization, but should have restored all the other fields correctly.
+ NetworkSelectionStatus expectedStatus = new NetworkSelectionStatus();
+ expectedStatus.copy(status);
+ expectedStatus.setNetworkSelectionStatus(NetworkSelectionStatus.NETWORK_SELECTION_ENABLED);
+ expectedStatus.setNetworkSelectionDisableReason(
+ NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
+
+ WifiConfigurationTestUtil.assertNetworkSelectionStatusEqualForConfigStore(
+ expectedStatus, retrievedStatus);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Verify that an illegal argument exception is thrown when trying to parse out a corrupted
+ * WifiEnterpriseConfig.
+ *
+ * @throws Exception
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testWifiEnterpriseConfigSerializeDeserializeThrowsIllegalArgException()
+ throws Exception {
+ 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);
+ String xmlString = new String(serializeWifiEnterpriseConfig(config));
+ // Manipulate the XML data to set the EAP method to None, this should raise an Illegal
+ // argument exception in WifiEnterpriseConfig.setEapMethod().
+ xmlString = xmlString.replaceAll(
+ String.format(XML_STRING_EAP_METHOD_REPLACE_FORMAT, TEST_EAP_METHOD),
+ String.format(XML_STRING_EAP_METHOD_REPLACE_FORMAT, WifiEnterpriseConfig.Eap.NONE));
+ deserializeWifiEnterpriseConfig(xmlString.getBytes(StandardCharsets.UTF_8));
+ }
+
+ /**
+ * Verify that WifiConfiguration representation of a legacy Passpoint configuration is
+ * serialized & deserialized correctly.
+ *
+ *@throws Exception
+ */
+ @Test
+ public void testLegacyPasspointConfigSerializeDeserialize() throws Exception {
+ WifiConfiguration config = WifiConfigurationTestUtil.createPasspointNetwork();
+ config.isLegacyPasspointConfig = true;
+ config.roamingConsortiumIds = new long[] {0x12345678};
+ config.enterpriseConfig.setPlmn("1234");
+ config.enterpriseConfig.setRealm("test.com");
+ serializeDeserializeWifiConfigurationForConfigStore(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 byte[] serializeNetworkSelectionStatus(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);
+ return outputStream.toByteArray();
+ }
+
+ private NetworkSelectionStatus deserializeNetworkSelectionStatus(byte[] data)
+ throws IOException, XmlPullParserException {
+ final XmlPullParser in = Xml.newPullParser();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
+ return NetworkSelectionStatusXmlUtil.parseFromXml(in, in.getDepth());
+ }
+
+ private void serializeDeserializeNetworkSelectionStatus(NetworkSelectionStatus status)
+ throws IOException, XmlPullParserException {
+ // Serialize the status object.
+ byte[] data = serializeNetworkSelectionStatus(status);
+ // Deserialize the status object.
+ NetworkSelectionStatus retrievedStatus = deserializeNetworkSelectionStatus(data);
+
+ WifiConfigurationTestUtil.assertNetworkSelectionStatusEqualForConfigStore(
+ status, retrievedStatus);
+ }
+
+ private byte[] serializeWifiEnterpriseConfig(WifiEnterpriseConfig config)
+ throws IOException, XmlPullParserException {
+ 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);
+ return outputStream.toByteArray();
+ }
+
+ private WifiEnterpriseConfig deserializeWifiEnterpriseConfig(byte[] data)
+ throws IOException, XmlPullParserException {
+ final XmlPullParser in = Xml.newPullParser();
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ in.setInput(inputStream, StandardCharsets.UTF_8.name());
+ XmlUtil.gotoDocumentStart(in, mXmlDocHeader);
+ return WifiEnterpriseConfigXmlUtil.parseFromXml(in, in.getDepth());
+ }
+
+ private void serializeDeserializeWifiEnterpriseConfig(WifiEnterpriseConfig config)
+ throws IOException, XmlPullParserException {
+ WifiEnterpriseConfig retrievedConfig =
+ deserializeWifiEnterpriseConfig(serializeWifiEnterpriseConfig(config));
+ WifiConfigurationTestUtil.assertWifiEnterpriseConfigEqualForConfigStore(
+ config, retrievedConfig);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/wificond/NativeScanResultTest.java b/tests/wifitests/src/com/android/server/wifi/wificond/NativeScanResultTest.java
new file mode 100644
index 0000000..08573e4
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/wificond/NativeScanResultTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.wificond;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.util.BitSet;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.wificond.NativeScanResult}.
+ */
+@SmallTest
+public class NativeScanResultTest {
+
+ private static final byte[] TEST_SSID =
+ new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'};
+ private static final byte[] TEST_BSSID =
+ new byte[] {(byte) 0x12, (byte) 0xef, (byte) 0xa1,
+ (byte) 0x2c, (byte) 0x97, (byte) 0x8b};
+ private static final byte[] TEST_INFO_ELEMENT =
+ new byte[] {(byte) 0x01, (byte) 0x03, (byte) 0x12, (byte) 0xbe, (byte) 0xff};
+ private static final int TEST_FREQUENCY = 2456;
+ private static final int TEST_SIGNAL_MBM = -45;
+ private static final long TEST_TSF = 34455441;
+ private static final BitSet TEST_CAPABILITY = new BitSet(16) {{ set(2); set(5); }};
+ private static final boolean TEST_ASSOCIATED = true;
+
+ /**
+ * NativeScanResult object can be serialized and deserialized, while keeping the
+ * values unchanged.
+ */
+ @Test
+ public void canSerializeAndDeserialize() throws Exception {
+ NativeScanResult scanResult = new NativeScanResult();
+ scanResult.ssid = TEST_SSID;
+ scanResult.bssid = TEST_BSSID;
+ scanResult.infoElement = TEST_INFO_ELEMENT;
+ scanResult.frequency = TEST_FREQUENCY;
+ scanResult.signalMbm = TEST_SIGNAL_MBM;
+ scanResult.tsf = TEST_TSF;
+ scanResult.capability = TEST_CAPABILITY;
+ scanResult.associated = TEST_ASSOCIATED;
+ Parcel parcel = Parcel.obtain();
+ scanResult.writeToParcel(parcel, 0);
+ // Rewind the pointer to the head of the parcel.
+ parcel.setDataPosition(0);
+ NativeScanResult scanResultDeserialized = NativeScanResult.CREATOR.createFromParcel(parcel);
+
+ assertArrayEquals(scanResult.ssid, scanResultDeserialized.ssid);
+ assertArrayEquals(scanResult.bssid, scanResultDeserialized.bssid);
+ assertArrayEquals(scanResult.infoElement, scanResultDeserialized.infoElement);
+ assertEquals(scanResult.frequency, scanResultDeserialized.frequency);
+ assertEquals(scanResult.signalMbm, scanResultDeserialized.signalMbm);
+ assertEquals(scanResult.tsf, scanResultDeserialized.tsf);
+ assertTrue(scanResult.capability.equals(scanResultDeserialized.capability));
+ assertEquals(scanResult.associated, scanResultDeserialized.associated);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/wificond/PnoSettingsTest.java b/tests/wifitests/src/com/android/server/wifi/wificond/PnoSettingsTest.java
new file mode 100644
index 0000000..ee40197
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/wificond/PnoSettingsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.wificond;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.wificond.PnoSettingsResult}.
+ */
+@SmallTest
+public class PnoSettingsTest {
+
+ private static final byte[] TEST_SSID_1 =
+ new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'};
+ private static final byte[] TEST_SSID_2 =
+ new byte[] {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'T', 'e', 's', 't'};
+ private static final int TEST_INTERVAL_MS = 30000;
+ private static final int TEST_MIN_2G_RSSI = -60;
+ private static final int TEST_MIN_5G_RSSI = -65;
+
+ /**
+ * PnoSettings object can be serialized and deserialized, while keeping the
+ * values unchanged.
+ */
+ @Test
+ public void canSerializeAndDeserialize() throws Exception {
+
+ PnoSettings pnoSettings = new PnoSettings();
+
+ PnoNetwork pnoNetwork1 = new PnoNetwork();
+ pnoNetwork1.ssid = TEST_SSID_1;
+ pnoNetwork1.isHidden = true;
+
+ PnoNetwork pnoNetwork2 = new PnoNetwork();
+ pnoNetwork2.ssid = TEST_SSID_2;
+ pnoNetwork2.isHidden = false;
+
+ pnoSettings.pnoNetworks = new ArrayList(Arrays.asList(pnoNetwork1, pnoNetwork2));
+
+ pnoSettings.intervalMs = TEST_INTERVAL_MS;
+ pnoSettings.min2gRssi = TEST_MIN_2G_RSSI;
+ pnoSettings.min5gRssi = TEST_MIN_5G_RSSI;
+
+ Parcel parcel = Parcel.obtain();
+ pnoSettings.writeToParcel(parcel, 0);
+ // Rewind the pointer to the head of the parcel.
+ parcel.setDataPosition(0);
+ PnoSettings pnoSettingsDeserialized =
+ pnoSettings.CREATOR.createFromParcel(parcel);
+
+ assertNotNull(pnoSettingsDeserialized);
+ assertEquals(pnoSettings, pnoSettingsDeserialized);
+ }
+}
diff --git a/tests/wifitests/src/com/android/server/wifi/wificond/SingleScanSettingsTest.java b/tests/wifitests/src/com/android/server/wifi/wificond/SingleScanSettingsTest.java
new file mode 100644
index 0000000..0a63616
--- /dev/null
+++ b/tests/wifitests/src/com/android/server/wifi/wificond/SingleScanSettingsTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi.wificond;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link com.android.server.wifi.wificond.SingleScanSettingsResult}.
+ */
+@SmallTest
+public class SingleScanSettingsTest {
+
+ private static final byte[] TEST_SSID_1 =
+ new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'};
+ private static final byte[] TEST_SSID_2 =
+ new byte[] {'A', 'n', 'd', 'r', 'o', 'i', 'd', 'T', 'e', 's', 't'};
+ private static final int TEST_FREQUENCY_1 = 2456;
+ private static final int TEST_FREQUENCY_2 = 5215;
+
+ /**
+ * SingleScanSettings object can be serialized and deserialized, while keeping the
+ * values unchanged.
+ */
+ @Test
+ public void canSerializeAndDeserialize() throws Exception {
+ ChannelSettings channelSettings1 = new ChannelSettings();
+ channelSettings1.frequency = TEST_FREQUENCY_1;
+ ChannelSettings channelSettings2 = new ChannelSettings();
+ channelSettings2.frequency = TEST_FREQUENCY_2;
+
+ HiddenNetwork hiddenNetwork1 = new HiddenNetwork();
+ hiddenNetwork1.ssid = TEST_SSID_1;
+ HiddenNetwork hiddenNetwork2 = new HiddenNetwork();
+ hiddenNetwork2.ssid = TEST_SSID_2;
+
+ SingleScanSettings scanSettings = new SingleScanSettings();
+ scanSettings.channelSettings =
+ new ArrayList(Arrays.asList(channelSettings1, channelSettings2));
+ scanSettings.hiddenNetworks = new ArrayList(Arrays.asList(hiddenNetwork1, hiddenNetwork2));
+
+ Parcel parcel = Parcel.obtain();
+ scanSettings.writeToParcel(parcel, 0);
+ // Rewind the pointer to the head of the parcel.
+ parcel.setDataPosition(0);
+ SingleScanSettings scanSettingsDeserialized =
+ SingleScanSettings.CREATOR.createFromParcel(parcel);
+
+ assertNotNull(scanSettingsDeserialized);
+ assertEquals(scanSettings, scanSettingsDeserialized);
+ }
+}